Compare commits
199 Commits
v.1.5.3.14
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
d9d890975a | ||
|
8043fce1ce | ||
|
3677f6e268 | ||
|
0318700981 | ||
|
66cbbeed1a | ||
|
b262d28c10 | ||
|
38a9164e5c | ||
|
e472934bb4 | ||
|
6129780229 | ||
|
e821960c59 | ||
|
fa77d69bed | ||
|
7ab6a63a67 | ||
|
763a6a0763 | ||
|
dfbc95bd61 | ||
|
33387bd351 | ||
|
b02c4f48c3 | ||
|
006b9d575c | ||
|
4ebcd670e7 | ||
|
0b5daf162b | ||
|
11b5deecb8 | ||
|
fd849db239 | ||
|
6d9e735883 | ||
|
b5d3e5f066 | ||
|
a75f87e433 | ||
|
285615d67c | ||
|
eef8395205 | ||
|
465ab1ff23 | ||
|
1f51581ae3 | ||
|
87b547e724 | ||
|
3b83221cec | ||
|
f79855f8b2 | ||
|
1952c76533 | ||
|
92f0b1aaf5 | ||
|
ebea9d2692 | ||
|
a91ef76b64 | ||
|
0cd7f69931 | ||
|
b350812083 | ||
|
80ccc0b1d7 | ||
|
cc60c7adfb | ||
|
d61f6b8e99 | ||
|
7adc1f1cf5 | ||
|
7e177ee84c | ||
|
51046638d6 | ||
|
2522d44f13 | ||
|
018cab3ded | ||
|
a1714878a7 | ||
|
23b69ba121 | ||
|
9f6903e4e9 | ||
|
4c59ab5431 | ||
|
33d74e8e73 | ||
|
5f1ca7af51 | ||
|
56a5094881 | ||
|
cde810a9d0 | ||
|
73bb47f745 | ||
|
349d268189 | ||
|
3a8cbb07de | ||
|
800285f2cd | ||
|
d3add2561d | ||
|
621ad25a8a | ||
|
8dd8d7127d | ||
|
ce9b599501 | ||
|
28fc541891 | ||
|
cf2b693334 | ||
|
11672e9653 | ||
|
a42051bb40 | ||
|
aa620e1cf0 | ||
|
22bd9e3d7c | ||
|
6e774a1458 | ||
|
0e2078a268 | ||
|
51233e0cbe | ||
|
2ac5ec9feb | ||
|
bc6e8a9c08 | ||
|
39e0d0cfd6 | ||
|
a1a3def686 | ||
|
67804cad3c | ||
|
ce8f843746 | ||
|
0b954131b4 | ||
|
927bba6467 | ||
|
8f230e5c45 | ||
|
41238258ba | ||
|
1cf9be54c7 | ||
|
303a15fef3 | ||
|
04f93b193f | ||
|
fbf69cda19 | ||
|
8e42927880 | ||
|
4e254e42f7 | ||
|
cc72b93198 | ||
|
cc4783b85c | ||
|
5fd31999e7 | ||
|
9f9e7016e2 | ||
|
b96ba86be3 | ||
|
98ee26e353 | ||
|
e8244d61b7 | ||
|
87d2382828 | ||
|
03caf942b2 | ||
|
b215f3ba84 | ||
|
0f0225cfcd | ||
|
afb13bf976 | ||
|
06b8cf78d1 | ||
|
c2b979a05f | ||
|
4a967c0b80 | ||
|
ceb7d88cd9 | ||
|
17597fdaab | ||
|
702d468d2f | ||
|
f3d19fe95f | ||
|
69e81c4587 | ||
|
6ab743a2e2 | ||
|
b24c781a72 | ||
|
be3eb4033d | ||
|
0e7c0daebc | ||
|
3aa86f1e5a | ||
|
08b3f25f0b | ||
|
ecf1976837 | ||
|
6fd61b9591 | ||
|
a216d4bc9d | ||
|
935d817f6f | ||
|
b895bdec4f | ||
|
a5c665c275 | ||
|
82de23bb1a | ||
|
ab837561d9 | ||
|
14c73a71d2 | ||
|
3e39b99ed7 | ||
|
df49eac1da | ||
|
b8640f1f5a | ||
|
db9e02cf09 | ||
|
f529331698 | ||
|
3186e4322b | ||
|
6bda815669 | ||
|
c2b031efa5 | ||
|
d44119f9bf | ||
|
079043ff6a | ||
|
b6e743f032 | ||
|
818f2470e3 | ||
|
f36138d64e | ||
|
1da0717a45 | ||
|
f15289ad98 | ||
|
958b03bd5a | ||
|
218872ba75 | ||
|
53ea4cef57 | ||
|
85dfaff25f | ||
|
dadab1ad13 | ||
|
1e68e01e39 | ||
|
da9e6aaf6b | ||
|
a35551906a | ||
|
ca1cf9ed21 | ||
|
b69496c0f3 | ||
|
d03132d2ab | ||
|
3512bbb1eb | ||
|
e563094714 | ||
|
b32754134f | ||
|
0bf927a43f | ||
|
f6a3e98765 | ||
|
4f7969176f | ||
|
6272936e14 | ||
|
2c8597d742 | ||
|
56e26d438e | ||
|
e62a140698 | ||
|
ee827731b6 | ||
|
5de0630bb4 | ||
|
6d20b3dad2 | ||
|
b624999636 | ||
|
4f534a2c44 | ||
|
eb30f9a4e1 | ||
|
497c7c0678 | ||
|
a3160c12af | ||
|
59c80ca856 | ||
|
23be190e21 | ||
|
f86bee6768 | ||
|
65cde67b49 | ||
|
5c84c4cb91 | ||
|
6094be47f2 | ||
|
ba96930432 | ||
|
c0db39990d | ||
|
acc0130e7c | ||
|
35e1fc38cd | ||
|
9b39111f46 | ||
|
70ba5d9f24 | ||
|
782d003a91 | ||
|
c834d181a6 | ||
|
16873a9175 | ||
|
a13747b64f | ||
|
2b9a497eb0 | ||
|
22b0f83992 | ||
|
ae52548113 | ||
|
bfd7f5cef7 | ||
|
8d3a0f8c73 | ||
|
889874ecaf | ||
|
cfe2e6afd1 | ||
|
265705f087 | ||
|
b4b1001b70 | ||
|
92ab1a627c | ||
|
e3fafd5775 | ||
|
5061113bdd | ||
|
166b8adb62 | ||
|
abcfb93964 | ||
|
d5d6bd8910 | ||
|
3ef54e979b | ||
|
acc44fb83f | ||
|
d5e914634d |
4
.github/ISSUE_TEMPLATE/issue.yaml
vendored
4
.github/ISSUE_TEMPLATE/issue.yaml
vendored
@ -1,4 +1,4 @@
|
||||
name: Issue or Bug Report
|
||||
name: Issue or Bug Report for v.1.x.x.x
|
||||
description: Please provide as much detail as possible to convey the history of your problem.
|
||||
title: "[ISSUE]: "
|
||||
body:
|
||||
@ -34,7 +34,7 @@ body:
|
||||
label: Read carefully and check the options
|
||||
options:
|
||||
- label: I've tried to Clear Settings
|
||||
- label: Sample/Default Models are working
|
||||
- label: Sample/Default Models are working
|
||||
- label: I've tried to change the Chunk Size
|
||||
- label: GUI was successfully launched
|
||||
- label: I've read the [tutorial](https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_en_latest.md)
|
||||
|
82
.github/ISSUE_TEMPLATE/issue_for_v2.yaml
vendored
Normal file
82
.github/ISSUE_TEMPLATE/issue_for_v2.yaml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: Issue or Bug Report for v.2.x.x
|
||||
description: Please provide as much detail as possible to convey the history of your problem.
|
||||
title: "[ISSUE for v2]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Please read our [FAQ](https://github.com/w-okada/voice-changer/blob/master/.github/FAQ.md) before making a bug report!
|
||||
- type: input
|
||||
id: vc-client-version
|
||||
attributes:
|
||||
label: Voice Changer Version
|
||||
description: Downloaded File Name (.zip)
|
||||
placeholder: vcclient_win_std_x.y.x.zip, vcclient_win_cuda_torch_cuda_x.y.x.zip, or so
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: OS
|
||||
attributes:
|
||||
label: Operational System
|
||||
description: e.g. Windows 10, Ubuntu 20.04, MacOS Venture, MacOS Monterey, etc...
|
||||
placeholder: Windows 10
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: GPU
|
||||
attributes:
|
||||
label: GPU
|
||||
description: If you have no gpu, please input none.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: CUDA
|
||||
attributes:
|
||||
label: CUDA Version
|
||||
description: If you have nvidia gpu, please input version of cuda. Otherwise, please input none.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Read carefully and check the options
|
||||
options:
|
||||
- label: If you use win_cuda_torch_cuda edition, setup cuda? [see here](https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html#requirements)
|
||||
- label: If you use win_cuda edition, setup cuda and cudnn? [see here](https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html#requirements)
|
||||
- label: If you use mac edition, client is not launched automatically. Use chrome to open application.?
|
||||
- label: I've tried to change the Chunk Size
|
||||
- label: I've tried to set the Index to zero
|
||||
- label: I've read the [tutorial](https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_en_latest.md)
|
||||
- label: I've tried to extract to another folder (or re-extract) the .zip file
|
||||
- type: dropdown
|
||||
id: sample-model-work
|
||||
attributes:
|
||||
label: Does pre-installed model work?
|
||||
options:
|
||||
- "No"
|
||||
- "YES"
|
||||
default: 0
|
||||
- type: input
|
||||
id: vc-type
|
||||
attributes:
|
||||
label: Model Type
|
||||
description: MMVC, so-vits-rvc, RVC, DDSP-SVC
|
||||
placeholder: RVC
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue
|
||||
attributes:
|
||||
label: Issue Description
|
||||
description: Please provide as much reproducible information and logs as possible
|
||||
- type: textarea
|
||||
id: capture
|
||||
attributes:
|
||||
label: Application Screenshot
|
||||
description: Please provide a screenshot of your application so we can see your settings (you can paste or drag-n-drop)
|
||||
- type: textarea
|
||||
id: logs-on-terminal
|
||||
attributes:
|
||||
label: Logs on console
|
||||
description: Copy and paste the log on your console here
|
||||
validations:
|
||||
required: true
|
2
.github/ISSUE_TEMPLATE/question.yaml
vendored
2
.github/ISSUE_TEMPLATE/question.yaml
vendored
@ -1,3 +1,5 @@
|
||||
情報なしの質問が来てしまうので、このフォームを一時的に無効にする。
|
||||
|
||||
name: Question or Other
|
||||
description: Do you any question? Use this template
|
||||
body:
|
||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -36,6 +36,11 @@ server/memo.md
|
||||
|
||||
client/lib/dist
|
||||
client/lib/worklet/dist
|
||||
client/demo/public/models
|
||||
client/demo/public/models_
|
||||
client/demo/dist/models
|
||||
client/demo/dist_web
|
||||
client/demo/src/001_provider/backup
|
||||
# client/demo/dist/ # demo用に残す
|
||||
|
||||
docker/cudnn/
|
||||
@ -53,6 +58,9 @@ server/samples_0003_o.json
|
||||
server/samples_0003_t2.json
|
||||
server/samples_0003_o2.json
|
||||
server/samples_0003_d2.json
|
||||
server/samples_0004_t.json
|
||||
server/samples_0004_o.json
|
||||
server/samples_0004_d.json
|
||||
|
||||
server/test_official_v1_v2.json
|
||||
server/test_ddpn_v1_v2.json
|
||||
@ -63,4 +71,7 @@ start_trainer.sh
|
||||
venv/
|
||||
|
||||
|
||||
beatrice_internal_api.cp310-win_amd64.pyd
|
||||
beatrice_internal_api.cp310-win_amd64.pyd
|
||||
108_average_110b_10.bin
|
||||
|
||||
server/model_dir_static/Beatrice-JVS
|
||||
|
55
Checklist.md
55
Checklist.md
@ -1,55 +0,0 @@
|
||||
# Release Check List
|
||||
## Run
|
||||
- [ ] Anaconda on Linux
|
||||
- [ ] Docker on Linux
|
||||
- [ ] Anaconda on WSL2
|
||||
- [ ] Docker on WSL2
|
||||
- [ ] Colab simple
|
||||
- [ ] Colab normal
|
||||
- [ ] Windows exe
|
||||
- [ ] Mac(M1)
|
||||
|
||||
## Doc
|
||||
- [ ] Readme
|
||||
- [ ] Wiki
|
||||
- [ ] Zenn
|
||||
|
||||
|
||||
# Memo
|
||||
## Release Process
|
||||
一通り開発が終わったと思ったら.
|
||||
|
||||
(1) Dockerを生成
|
||||
```
|
||||
npm run build:docker
|
||||
npm run push:docker
|
||||
```
|
||||
Tagをメモ。
|
||||
|
||||
(2) start2.shを編集
|
||||
メモしたTagを貼り付け。
|
||||
```
|
||||
bash start2.sh
|
||||
```
|
||||
|
||||
(3) exeファイル作成
|
||||
(3-1) Win
|
||||
・環境変数にリリースバージョンを設定
|
||||
・pipenv
|
||||
|
||||
(4) Readmeにリンクをはる
|
||||
|
||||
(5) Branch 解除。Tag化
|
||||
```
|
||||
git add ...
|
||||
git commit -m "wip: releasing"
|
||||
git push
|
||||
git checkout - && git merge - && git push && git checkout -
|
||||
git checkout -
|
||||
git branch -d v.1...
|
||||
git tag v.1...
|
||||
git push origin v.1
|
||||
git branch v.1....
|
||||
git checkout v.1...
|
||||
```
|
||||
(6) Colabチェック
|
1
Hina_Mod_Kaggle_Real_Time_Voice_Changer.ipynb
Normal file
1
Hina_Mod_Kaggle_Real_Time_Voice_Changer.ipynb
Normal file
File diff suppressed because one or more lines are too long
351
Hina_Modified_Realtime_Voice_Changer_on_Colab.ipynb
Normal file
351
Hina_Modified_Realtime_Voice_Changer_on_Colab.ipynb
Normal file
@ -0,0 +1,351 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view-in-github",
|
||||
"colab_type": "text"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/hinabl/voice-changer-colab/blob/master/Hina_Modified_Realtime_Voice_Changer_on_Colab.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Lbbmx_Vjl0zo"
|
||||
},
|
||||
"source": [
|
||||
"### w-okada's Voice Changer | **Google Colab**\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"##**READ ME - VERY IMPORTANT**\n",
|
||||
"\n",
|
||||
"This is an attempt to run [Realtime Voice Changer](https://github.com/w-okada/voice-changer) on Google Colab, still not perfect but is totally usable, you can use the following settings for better results:\n",
|
||||
"\n",
|
||||
"If you're using a index: `f0: RMVPE_ONNX | Chunk: 112 or higher | Extra: 8192`\\\n",
|
||||
"If you're not using a index: `f0: RMVPE_ONNX | Chunk: 96 or higher | Extra: 16384`\\\n",
|
||||
"**Don't forget to select your Colab GPU in the GPU field (<b>Tesla T4</b>, for free users)*\n",
|
||||
"> Seems that PTH models performance better than ONNX for now, you can still try ONNX models and see if it satisfies you\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*You can always [click here](https://rentry.co/VoiceChangerGuide#gpu-chart-for-known-working-chunkextra\n",
|
||||
") to check if these settings are up-to-date*\n",
|
||||
"<br><br>\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"###Always use Colab GPU (**VERY VERY VERY IMPORTANT!**)\n",
|
||||
"You need to use a Colab GPU so the Voice Changer can work faster and better\\\n",
|
||||
"Use the menu above and click on **Runtime** » **Change runtime** » **Hardware acceleration** to select a GPU (**T4 is the free one**)\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"# **Credits and Support**\n",
|
||||
"Realtime Voice Changer by [w-okada](https://github.com/w-okada)\\\n",
|
||||
"Colab files updated by [rafacasari](https://github.com/Rafacasari)\\\n",
|
||||
"Recommended settings by [Raven](https://github.com/ravencutie21)\\\n",
|
||||
"Modified again by [Hina](https://huggingface.co/HinaBl)\n",
|
||||
"\n",
|
||||
"Need help? [AI Hub Discord](https://discord.gg/aihub) » ***#help-realtime-vc***\n",
|
||||
"\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "86wTFmqsNMnD",
|
||||
"cellView": "form"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#=================Updated=================\n",
|
||||
"# @title **[1]** Clone repository and install dependencies\n",
|
||||
"# @markdown This first step will download the latest version of Voice Changer and install the dependencies. **It can take some time to complete.**\n",
|
||||
"import os\n",
|
||||
"import time\n",
|
||||
"import subprocess\n",
|
||||
"import threading\n",
|
||||
"import shutil\n",
|
||||
"import base64\n",
|
||||
"import codecs\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#@markdown ---\n",
|
||||
"# @title **[Optional]** Connect to Google Drive\n",
|
||||
"# @markdown Using Google Drive can improve load times a bit and your models will be stored, so you don't need to re-upload every time that you use.\n",
|
||||
"\n",
|
||||
"Use_Drive=False #@param {type:\"boolean\"}\n",
|
||||
"\n",
|
||||
"from google.colab import drive\n",
|
||||
"\n",
|
||||
"if Use_Drive==True:\n",
|
||||
" if not os.path.exists('/content/drive'):\n",
|
||||
" drive.mount('/content/drive')\n",
|
||||
"\n",
|
||||
" %cd /content/drive/MyDrive\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"externalgit=codecs.decode('uggcf://tvguho.pbz/j-bxnqn/ibvpr-punatre.tvg','rot_13')\n",
|
||||
"rvctimer=codecs.decode('uggcf://tvguho.pbz/uvanoy/eipgvzre.tvg','rot_13')\n",
|
||||
"pathloc=codecs.decode('ibvpr-punatre','rot_13')\n",
|
||||
"\n",
|
||||
"from IPython.display import clear_output, Javascript\n",
|
||||
"\n",
|
||||
"def update_timer_and_print():\n",
|
||||
" global timer\n",
|
||||
" while True:\n",
|
||||
" hours, remainder = divmod(timer, 3600)\n",
|
||||
" minutes, seconds = divmod(remainder, 60)\n",
|
||||
" timer_str = f'{hours:02}:{minutes:02}:{seconds:02}'\n",
|
||||
" print(f'\\rTimer: {timer_str}', end='', flush=True) # Print without a newline\n",
|
||||
" time.sleep(1)\n",
|
||||
" timer += 1\n",
|
||||
"timer = 0\n",
|
||||
"threading.Thread(target=update_timer_and_print, daemon=True).start()\n",
|
||||
"\n",
|
||||
"!pip install colorama --quiet\n",
|
||||
"from colorama import Fore, Style\n",
|
||||
"\n",
|
||||
"print(f\"{Fore.CYAN}> Cloning the repository...{Style.RESET_ALL}\")\n",
|
||||
"!git clone --depth 1 $externalgit &> /dev/null\n",
|
||||
"print(f\"{Fore.GREEN}> Successfully cloned the repository!{Style.RESET_ALL}\")\n",
|
||||
"%cd $pathloc/server/\n",
|
||||
"\n",
|
||||
"# Read the content of the file\n",
|
||||
"file_path = '../client/demo/dist/assets/gui_settings/version.txt'\n",
|
||||
"\n",
|
||||
"with open(file_path, 'r') as file:\n",
|
||||
" file_content = file.read()\n",
|
||||
"\n",
|
||||
"# Replace the specific text\n",
|
||||
"text_to_replace = \"-.-.-.-\"\n",
|
||||
"new_text = \"Google.Colab\" # New text to replace the specific text\n",
|
||||
"\n",
|
||||
"modified_content = file_content.replace(text_to_replace, new_text)\n",
|
||||
"\n",
|
||||
"# Write the modified content back to the file\n",
|
||||
"with open(file_path, 'w') as file:\n",
|
||||
" file.write(modified_content)\n",
|
||||
"\n",
|
||||
"print(f\"Text '{text_to_replace}' has been replaced with '{new_text}' in the file.\")\n",
|
||||
"\n",
|
||||
"print(f\"{Fore.CYAN}> Installing libportaudio2...{Style.RESET_ALL}\")\n",
|
||||
"!apt-get -y install libportaudio2 -qq\n",
|
||||
"\n",
|
||||
"!sed -i '/torch==/d' requirements.txt\n",
|
||||
"!sed -i '/torchaudio==/d' requirements.txt\n",
|
||||
"!sed -i '/numpy==/d' requirements.txt\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(f\"{Fore.CYAN}> Installing pre-dependencies...{Style.RESET_ALL}\")\n",
|
||||
"# Install dependencies that are missing from requirements.txt and pyngrok\n",
|
||||
"!pip install faiss-gpu fairseq pyngrok --quiet\n",
|
||||
"!pip install pyworld --no-build-isolation --quiet\n",
|
||||
"# Install webstuff\n",
|
||||
"import asyncio\n",
|
||||
"import re\n",
|
||||
"!pip install playwright\n",
|
||||
"!playwright install\n",
|
||||
"!playwright install-deps\n",
|
||||
"!pip install nest_asyncio\n",
|
||||
"from playwright.async_api import async_playwright\n",
|
||||
"print(f\"{Fore.CYAN}> Installing dependencies from requirements.txt...{Style.RESET_ALL}\")\n",
|
||||
"!pip install -r requirements.txt --quiet\n",
|
||||
"clear_output()\n",
|
||||
"print(f\"{Fore.GREEN}> Successfully installed all packages!{Style.RESET_ALL}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"#@title **[Optional]** Upload a voice model (Run this before running the Voice Changer)\n",
|
||||
"import os\n",
|
||||
"import json\n",
|
||||
"from IPython.display import Image\n",
|
||||
"import requests\n",
|
||||
"\n",
|
||||
"model_slot = \"0\" #@param ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100', '101', '102', '103', '104', '105', '106', '107', '108', '109', '110', '111', '112', '113', '114', '115', '116', '117', '118', '119', '120', '121', '122', '123', '124', '125', '126', '127', '128', '129', '130', '131', '132', '133', '134', '135', '136', '137', '138', '139', '140', '141', '142', '143', '144', '145', '146', '147', '148', '149', '150', '151', '152', '153', '154', '155', '156', '157', '158', '159', '160', '161', '162', '163', '164', '165', '166', '167', '168', '169', '170', '171', '172', '173', '174', '175', '176', '177', '178', '179', '180', '181', '182', '183', '184', '185', '186', '187', '188', '189', '190', '191', '192', '193', '194', '195', '196', '197', '198', '199']\n",
|
||||
"\n",
|
||||
"!rm -rf model_dir/$model_slot\n",
|
||||
"#@markdown **[Optional]** Add an icon to the model\n",
|
||||
"icon_link = \"https://cdn.donmai.us/sample/12/57/__rin_penrose_idol_corp_drawn_by_juu_ame__sample-12579843de9487cf2db82058ba5e77d4.jpg\" #@param {type:\"string\"}\n",
|
||||
"icon_link = '\"'+icon_link+'\"'\n",
|
||||
"!mkdir model_dir\n",
|
||||
"!mkdir model_dir/$model_slot\n",
|
||||
"#@markdown Put your model's download link here `(must be a zip file)` only supports **weights.gg** & **huggingface.co**\n",
|
||||
"model_link = \"https://huggingface.co/HinaBl/Rin-Penrose/resolve/main/RinPenrose600.zip?download=true\" #@param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"if model_link.startswith(\"https://www.weights.gg\") or model_link.startswith(\"https://weights.gg\"):\n",
|
||||
" weights_code = requests.get(\"https://pastebin.com/raw/ytHLr8h0\").text\n",
|
||||
" exec(weights_code)\n",
|
||||
"else:\n",
|
||||
" model_link = model_link\n",
|
||||
"\n",
|
||||
"model_link = '\"'+model_link+'\"'\n",
|
||||
"!curl -L $model_link > model.zip\n",
|
||||
"\n",
|
||||
"# Conditionally set the iconFile based on whether icon_link is empty\n",
|
||||
"if icon_link:\n",
|
||||
" iconFile = \"icon.png\"\n",
|
||||
" !curl -L $icon_link > model_dir/$model_slot/icon.png\n",
|
||||
"else:\n",
|
||||
" iconFile = \"\"\n",
|
||||
" print(\"icon_link is empty, so no icon file will be downloaded.\")\n",
|
||||
"\n",
|
||||
"!unzip model.zip -d model_dir/$model_slot\n",
|
||||
"\n",
|
||||
"!mv model_dir/$model_slot/*/* model_dir/$model_slot/\n",
|
||||
"!rm -rf model_dir/$model_slot/*/\n",
|
||||
"#@markdown **Model Voice Convertion Setting**\n",
|
||||
"Tune = 12 #@param {type:\"slider\",min:-50,max:50,step:1}\n",
|
||||
"Index = 0 #@param {type:\"slider\",min:0,max:1,step:0.1}\n",
|
||||
"\n",
|
||||
"param_link = \"\"\n",
|
||||
"if param_link == \"\":\n",
|
||||
" paramset = requests.get(\"https://pastebin.com/raw/SAKwUCt1\").text\n",
|
||||
" exec(paramset)\n",
|
||||
"\n",
|
||||
"clear_output()\n",
|
||||
"print(\"\\033[93mModel with the name of \"+model_name+\" has been Imported to slot \"+model_slot)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "_ZtbKUVUgN3G",
|
||||
"cellView": "form"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lLWQuUd7WW9U",
|
||||
"cellView": "form"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"#=======================Updated=========================\n",
|
||||
"\n",
|
||||
"# @title Start Server **using ngrok**\n",
|
||||
"# @markdown This cell will start the server, the first time that you run it will download the models, so it can take a while (~1-2 minutes)\n",
|
||||
"\n",
|
||||
"# @markdown ---\n",
|
||||
"# @markdown You'll need a ngrok account, but <font color=green>**it's free**</font> and easy to create!\n",
|
||||
"# @markdown ---\n",
|
||||
"# @markdown **1** - Create a <font color=green>**free**</font> account at [ngrok](https://dashboard.ngrok.com/signup) or **login with Google/Github account**\\\n",
|
||||
"# @markdown **2** - If you didn't logged in with Google/Github, you will need to **verify your e-mail**!\\\n",
|
||||
"# @markdown **3** - Click [this link](https://dashboard.ngrok.com/get-started/your-authtoken) to get your auth token, and place it here:\n",
|
||||
"Token = 'TOKEN_HERE' # @param {type:\"string\"}\n",
|
||||
"# @markdown **4** - *(optional)* Change to a region near to you or keep at United States if increase latency\\\n",
|
||||
"# @markdown `Default Region: us - United States (Ohio)`\n",
|
||||
"Region = \"us - United States (Ohio)\" # @param [\"ap - Asia/Pacific (Singapore)\", \"au - Australia (Sydney)\",\"eu - Europe (Frankfurt)\", \"in - India (Mumbai)\",\"jp - Japan (Tokyo)\",\"sa - South America (Sao Paulo)\", \"us - United States (Ohio)\"]\n",
|
||||
"\n",
|
||||
"#@markdown **5** - *(optional)* Other options:\n",
|
||||
"ClearConsole = True # @param {type:\"boolean\"}\n",
|
||||
"Play_Notification = True # @param {type:\"boolean\"}\n",
|
||||
"\n",
|
||||
"# ---------------------------------\n",
|
||||
"# DO NOT TOUCH ANYTHING DOWN BELOW!\n",
|
||||
"# ---------------------------------\n",
|
||||
"\n",
|
||||
"%cd $pathloc/server/\n",
|
||||
"\n",
|
||||
"from pyngrok import conf, ngrok\n",
|
||||
"MyConfig = conf.PyngrokConfig()\n",
|
||||
"MyConfig.auth_token = Token\n",
|
||||
"MyConfig.region = Region[0:2]\n",
|
||||
"#conf.get_default().authtoken = Token\n",
|
||||
"#conf.get_default().region = Region\n",
|
||||
"conf.set_default(MyConfig);\n",
|
||||
"\n",
|
||||
"import subprocess, threading, time, socket, urllib.request\n",
|
||||
"PORT = 8000\n",
|
||||
"\n",
|
||||
"from pyngrok import ngrok\n",
|
||||
"ngrokConnection = ngrok.connect(PORT)\n",
|
||||
"public_url = ngrokConnection.public_url\n",
|
||||
"\n",
|
||||
"from IPython.display import clear_output\n",
|
||||
"from IPython.display import Audio, display\n",
|
||||
"def play_notification_sound():\n",
|
||||
" display(Audio(url='https://raw.githubusercontent.com/hinabl/rmvpe-ai-kaggle/main/custom/audios/notif.mp3', autoplay=True))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def wait_for_server():\n",
|
||||
" while True:\n",
|
||||
" time.sleep(0.5)\n",
|
||||
" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
|
||||
" result = sock.connect_ex(('127.0.0.1', PORT))\n",
|
||||
" if result == 0:\n",
|
||||
" break\n",
|
||||
" sock.close()\n",
|
||||
" if ClearConsole:\n",
|
||||
" clear_output()\n",
|
||||
" print(\"--------- SERVER READY! ---------\")\n",
|
||||
" print(\"Your server is available at:\")\n",
|
||||
" print(public_url)\n",
|
||||
" print(\"---------------------------------\")\n",
|
||||
" if Play_Notification==True:\n",
|
||||
" play_notification_sound()\n",
|
||||
"\n",
|
||||
"threading.Thread(target=wait_for_server, daemon=True).start()\n",
|
||||
"\n",
|
||||
"mainpy=codecs.decode('ZZIPFreireFVB.cl','rot_13')\n",
|
||||
"\n",
|
||||
"!python3 $mainpy \\\n",
|
||||
" -p {PORT} \\\n",
|
||||
" --https False \\\n",
|
||||
" --content_vec_500 pretrain/checkpoint_best_legacy_500.pt \\\n",
|
||||
" --content_vec_500_onnx pretrain/content_vec_500.onnx \\\n",
|
||||
" --content_vec_500_onnx_on true \\\n",
|
||||
" --hubert_base pretrain/hubert_base.pt \\\n",
|
||||
" --hubert_base_jp pretrain/rinna_hubert_base_jp.pt \\\n",
|
||||
" --hubert_soft pretrain/hubert/hubert-soft-0d54a1f4.pt \\\n",
|
||||
" --nsf_hifigan pretrain/nsf_hifigan/model \\\n",
|
||||
" --crepe_onnx_full pretrain/crepe_onnx_full.onnx \\\n",
|
||||
" --crepe_onnx_tiny pretrain/crepe_onnx_tiny.onnx \\\n",
|
||||
" --rmvpe pretrain/rmvpe.pt \\\n",
|
||||
" --model_dir model_dir \\\n",
|
||||
" --samples samples.json\n",
|
||||
"\n",
|
||||
"ngrok.disconnect(ngrokConnection.public_url)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
""
|
||||
],
|
||||
"metadata": {
|
||||
"id": "2Uu1sTSwTc7q"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"private_outputs": true,
|
||||
"gpuType": "T4",
|
||||
"include_colab_link": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"accelerator": "GPU"
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
@ -5,3 +5,9 @@
|
||||
Diffusion SVC and DDSP SVC uses DiffSinger Community Vocoders. Please check the license from the following link.
|
||||
Please place it on pretrain\\nsf_hifigan if you are using a different model.
|
||||
https://openvpi.github.io/vocoders/
|
||||
|
||||
2. Beatrice JVS Corpus Edition のライセンスについてはこちらを確認してください。
|
||||
[readme](https://github.com/w-okada/voice-changer/blob/master/server/voice_changer/Beatrice/)
|
||||
|
||||
Please check here for the license of the Beatrice JVS Corpus Edition.
|
||||
[readme](https://github.com/w-okada/voice-changer/blob/master/server/voice_changer/Beatrice/)
|
||||
|
@ -1,351 +0,0 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"include_colab_link": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"gpuClass": "standard",
|
||||
"accelerator": "GPU"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view-in-github",
|
||||
"colab_type": "text"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/w-okada/voice-changer/blob/master/MMVCTrainerFrontendDemo.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# このノートブックは管理されていません。\n",
|
||||
"# VCClientをColabで使用する場合は、次のノートブックをご使用ください。\n",
|
||||
"\n",
|
||||
"# This notebook is not being managed.\n",
|
||||
"# If you're using VCClient in Colab, please use the following notebook.\n",
|
||||
"\n",
|
||||
"https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "_41LkcbiwUFK"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Voice Changer Simple (デモ版)\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"このノートはVoice ChangerをColab上で動かすデモ版です。\n",
|
||||
"\n",
|
||||
"正式版はローカルPCのDocker上で動かすアプリケーションです。\n",
|
||||
"\n",
|
||||
"正式版は、多くの場合より少ないタイムラグで滑らかに音声を変換できます。\n",
|
||||
"\n",
|
||||
"詳細な使用方法はこちらの[リポジトリ](https://github.com/w-okada/voice-changer)からご確認ください。\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Lbbmx_Vjl0zo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# GPUを確認\n",
|
||||
"GPUを用いたほうが高速に処理が行えます。\n",
|
||||
"\n",
|
||||
"下記のコマンドでGPUが確認できない場合は、上のメニューから\n",
|
||||
"\n",
|
||||
"「ランタイム」→「ランタイムの変更」→「ハードウェア アクセラレータ」\n",
|
||||
"\n",
|
||||
"でGPUを選択してください。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "oUKi1NYMmXrr"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (1) GPUの確認\n",
|
||||
"!nvidia-smi"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vV1t7PBRm-o6",
|
||||
"outputId": "4f159c80-8114-4732-f5af-0856f7b5c39a"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Sat Dec 10 23:45:44 2022 \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2 |\n",
|
||||
"|-------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|===============================+======================+======================|\n",
|
||||
"| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 70C P0 31W / 70W | 0MiB / 15109MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=============================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+-----------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# リポジトリのクローン\n",
|
||||
"リポジトリをクローンします"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "sLBfykjBnjWc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (2) リポジトリのクローン\n",
|
||||
"!git clone --depth 1 https://github.com/w-okada/voice-changer.git -b dev\n",
|
||||
"%cd voice-changer/demo/\n",
|
||||
"\n",
|
||||
"!cp ../template/setting_mmvc_colab.json ../frontend/dist/assets/setting.json"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "86wTFmqsNMnD",
|
||||
"outputId": "3f84319c-0365-442a-fa3a-0a8aea701926"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Cloning into 'voice-changer'...\n",
|
||||
"remote: Enumerating objects: 1029, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (1029/1029), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (919/919), done.\u001b[K\n",
|
||||
"remote: Total 1029 (delta 21), reused 979 (delta 10), pack-reused 0\u001b[K\n",
|
||||
"Receiving objects: 100% (1029/1029), 71.87 MiB | 26.18 MiB/s, done.\n",
|
||||
"Resolving deltas: 100% (21/21), done.\n",
|
||||
"/content/voice-changer/demo\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# モジュールのインストール\n",
|
||||
"\n",
|
||||
"必要なモジュールをインストールします。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "8Na2PbLZSWgZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (3) 設定ファイルの確認\n",
|
||||
"!apt-get install -y espeak libsndfile1-dev &> /dev/null\n",
|
||||
"!pip install unidecode &> /dev/null\n",
|
||||
"!pip install phonemizer &> /dev/null\n",
|
||||
"!pip install retry &> /dev/null\n",
|
||||
"!pip install python-socketio &> /dev/null\n",
|
||||
"!pip install fastapi &> /dev/null\n",
|
||||
"!pip install python-multipart &> /dev/null\n",
|
||||
"!pip install uvicorn &> /dev/null\n",
|
||||
"!pip install websockets &> /dev/null\n",
|
||||
"!pip install pyOpenSSL &> /dev/null\n",
|
||||
"!pip install pyopenjtalk==0.2.0 &> /dev/null\n",
|
||||
"\n",
|
||||
"%cd MMVC_Trainer/monotonic_align/\n",
|
||||
"!python setup.py build_ext --inplace\n",
|
||||
"%cd ../../"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "LwZAAuqxX7yY",
|
||||
"outputId": "61526e29-97d9-4975-e921-4ca9ae5a153f",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
}
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"/content/voice-changer/demo/MMVC_Trainer/monotonic_align\n",
|
||||
"running build_ext\n",
|
||||
"building 'monotonic_align.core' extension\n",
|
||||
"creating build\n",
|
||||
"creating build/temp.linux-x86_64-3.8\n",
|
||||
"x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/lib/python3.8/dist-packages/numpy/core/include -I/usr/include/python3.8 -c core.c -o build/temp.linux-x86_64-3.8/core.o\n",
|
||||
"x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -g -fwrapv -O2 -Wl,-Bsymbolic-functions -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.8/core.o -o /content/voice-changer/demo/MMVC_Trainer/monotonic_align/monotonic_align/core.cpython-38-x86_64-linux-gnu.so\n",
|
||||
"/content/voice-changer/demo\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# サーバの起動\n",
|
||||
"\n",
|
||||
"サーバを起動します。(4-1)\n",
|
||||
"\n",
|
||||
"サーバの起動状況を確認します。(4-2)\n",
|
||||
"\n",
|
||||
"このセルは繰り返し実行することになるのでCtrl+Retでセルを実行してください。\n",
|
||||
"\n",
|
||||
"下記のようなテキストが表示されたら起動完了です。\n",
|
||||
"\n",
|
||||
"**`DEBUG:asyncio:Using selector: EpollSelector`**\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
" Phase name:__main__\n",
|
||||
" PHASE3:__main__\n",
|
||||
" PHASE1:__main__\n",
|
||||
"Start MMVC SocketIO Server\n",
|
||||
" CONFIG:None, MODEL:None\n",
|
||||
"DEBUG:asyncio:Using selector: EpollSelector\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "-_2OcN9Borke"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (4-1) サーバの起動\n",
|
||||
"import random\n",
|
||||
"PORT = 10000 + random.randint(1, 9999)\n",
|
||||
"LOG_FILE = f\"LOG_FILE_{PORT}\"\n",
|
||||
"\n",
|
||||
"get_ipython().system_raw(f'python3 MMVCServerSIO.py -p {PORT} --colab True >{LOG_FILE} 2>&1 &')\n",
|
||||
"#print(f\"PORT:{PORT}, LOG_FILE:{LOG_FILE}\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "G-nMdPxEW1rc"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (4-2) サーバの起動確認\n",
|
||||
"!sleep 5\n",
|
||||
"!tail -20 {LOG_FILE}"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "chu06KpAjEK6",
|
||||
"outputId": "51b4979c-91b7-4c95-eb4b-40ea001042a9"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"\u001b[32m Phase name:__main__\u001b[0m\n",
|
||||
"\u001b[32m PHASE3:__main__\u001b[0m\n",
|
||||
"\u001b[32m PHASE1:__main__\u001b[0m\n",
|
||||
"\u001b[17mStart MMVC SocketIO Server\u001b[0m\n",
|
||||
"\u001b[34m CONFIG:None, MODEL:None\u001b[0m\n",
|
||||
"DEBUG:asyncio:Using selector: EpollSelector\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# プロキシを起動\n",
|
||||
"ウェブサーバへのアクセスをするためのプロキシを起動します。\n",
|
||||
"\n",
|
||||
"表示されたURLをクリックして開くと別タブでアプリが開きます。\n",
|
||||
"\n",
|
||||
"Colabなので、ロードにある程度時間がかかります(30秒くらい)。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "WhxcFLQEpctq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (5) プロキシを起動\n",
|
||||
"from google.colab.output import eval_js\n",
|
||||
"proxy = eval_js( \"google.colab.kernel.proxyPort(\" + str(PORT) + \")\" )\n",
|
||||
"print(f\"{proxy}trainer/\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nkRjZm95l87C",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 34
|
||||
},
|
||||
"outputId": "bcca9fae-642c-42f6-f3a5-a8bb0789090f"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"https://vljlzcgk39q-496ff2e9c6d22116-17357-colab.googleusercontent.com/trainer/\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [],
|
||||
"metadata": {
|
||||
"id": "s1aWiBg-cTle"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
225
README.md
225
README.md
@ -1,140 +1,110 @@
|
||||
## VC Client
|
||||
[日本語](/README.md) /
|
||||
[英語](/docs_i18n/README_en.md) /
|
||||
[韓国語](/docs_i18n/README_ko.md)/
|
||||
[中国語](/docs_i18n/README_zh.md)/
|
||||
[ドイツ語](/docs_i18n/README_de.md)/
|
||||
[アラビア語](/docs_i18n/README_ar.md)/
|
||||
[ギリシャ語](/docs_i18n/README_el.md)/
|
||||
[スペイン語](/docs_i18n/README_es.md)/
|
||||
[フランス語](/docs_i18n/README_fr.md)/
|
||||
[イタリア語](/docs_i18n/README_it.md)/
|
||||
[ラテン語](/docs_i18n/README_la.md)/
|
||||
[マレー語](/docs_i18n/README_ms.md)/
|
||||
[ロシア語](/docs_i18n/README_ru.md)
|
||||
*日本語以外は機械翻訳です。
|
||||
|
||||
[English](/README_en.md)
|
||||
## VCClient
|
||||
|
||||
VCClientは、AIを用いてリアルタイム音声変換を行うソフトウェアです。
|
||||
|
||||
## What's New!
|
||||
- v.1.5.3.14
|
||||
- Improve:
|
||||
- onnx performance (need to be converted)
|
||||
- Some fixes:
|
||||
- change default f0 det to onnx_rmvpe
|
||||
- disable unrecommnded f0 det on direct ml
|
||||
- Experimental
|
||||
- Add 16k RVC Sample (experimental)
|
||||
* v.2.0.78-beta
|
||||
* bugfix: RVCモデルのアップロードエラーを回避
|
||||
* ver.1.x との同時起動ができるようになりました。
|
||||
* 選択できるchunk sizeを増やしました。
|
||||
|
||||
- v.1.5.3.13
|
||||
* v.2.0.77-beta (only for RTX 5090, experimental)
|
||||
* 関連モジュールを5090対応 (開発者がRTX5090未所持のため、動作未検証)
|
||||
* v.2.0.76-beta
|
||||
* new feature:
|
||||
* Beatrice: 話者マージの実装
|
||||
* Beatrice: オートピッチシフト
|
||||
* bugfix:
|
||||
* サーバモードのデバイス選択時の不具合対応
|
||||
* v.2.0.73-beta
|
||||
* new feature:
|
||||
* 編集したbeatrice modelのダウンロード
|
||||
* bugfix:
|
||||
* beatrice v2 のpitch, formantが反映されないバグを修正
|
||||
* Applio のembedderを使用しているモデルのONNXができないバグを修正
|
||||
|
||||
- Improve:
|
||||
- overwrite models when upload new model
|
||||
- Skip pass through confirm dialog
|
||||
- some bugfixes:
|
||||
## ダウンロードと関連リンク
|
||||
|
||||
- v.1.5.3.12a
|
||||
Windows版、 M1 Mac版はhugging faceのリポジトリからダウンロードできます。
|
||||
|
||||
- Improve:
|
||||
- Pass through confirm dialog
|
||||
- bugfix:
|
||||
- server mode: change chunk size causes tune change
|
||||
- show blank icon
|
||||
* [VCClient のリポジトリ](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
* [Light VCClient for Beatrice v2 のリポジトリ](https://huggingface.co/wok000/light_vcclient_beatrice/tree/main)
|
||||
|
||||
# VC Client とは
|
||||
*1 Linuxはリポジトリをcloneしてお使いください。
|
||||
|
||||
1. 各種音声変換 AI(VC, Voice Conversion)を用いてリアルタイム音声変換を行うためのクライアントソフトウェアです。サポートしている音声変換 AI は次のものになります。
|
||||
### 関連リンク
|
||||
|
||||
- サポートする音声変換 AI (サポート VC)
|
||||
- [MMVC](https://github.com/isletennos/MMVC_Trainer)
|
||||
- [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc)
|
||||
- [RVC(Retrieval-based-Voice-Conversion)](https://github.com/liujing04/Retrieval-based-Voice-Conversion-WebUI)
|
||||
- [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC)
|
||||
* [Beatrice V2 トレーニングコードのリポジトリ](https://huggingface.co/fierce-cats/beatrice-trainer)
|
||||
* [Beatrice V2 トレーニングコード Colab版](https://github.com/w-okada/beatrice-trainer-colab)
|
||||
|
||||
2. 本ソフトウェアは、ネットワークを介した利用も可能であり、ゲームなどの高負荷なアプリケーションと同時に使用する場合などに音声変換処理の負荷を外部にオフロードすることができます。
|
||||
### 関連ソフトウェア
|
||||
|
||||
* [リアルタイムボイスチェンジャ VCClient](https://github.com/w-okada/voice-changer)
|
||||
* [読み上げソフトウェア TTSClient](https://github.com/w-okada/ttsclient)
|
||||
* [リアルタイム音声認識ソフトウェア ASRClient](https://github.com/w-okada/asrclient)
|
||||
|
||||
## VC Clientの特徴
|
||||
|
||||
## 多様なAIモデルをサポート
|
||||
|
||||
| AIモデル | v.2 | v.1 | ライセンス |
|
||||
| ------------------------------------------------------------------------------------------------------------ | --------- | -------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| [RVC ](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/jp/README.ja.md) | supported | supported | リポジトリを参照してください。 |
|
||||
| [Beatrice v1](https://prj-beatrice.com/) | n/a | supported (only win) | [独自](https://github.com/w-okada/voice-changer/tree/master/server/voice_changer/Beatrice) |
|
||||
| [Beatrice v2](https://prj-beatrice.com/) | supported | n/a | [独自](https://huggingface.co/wok000/vcclient_model/blob/main/beatrice_v2_beta/readme.md) |
|
||||
| [MMVC](https://github.com/isletennos/MMVC_Trainer) | n/a | supported | リポジトリを参照してください。 |
|
||||
| [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc) | n/a | supported | リポジトリを参照してください。 |
|
||||
| [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC) | n/a | supported | リポジトリを参照してください。 |
|
||||
|
||||
## スタンドアロン、ネットワーク経由の両構成をサポート
|
||||
|
||||
ローカルPCで完結した音声変換も、ネットワークを介した音声変換もサポートしています。
|
||||
ネットワークを介した利用を行うことで、ゲームなどの高負荷なアプリケーションと同時に使用する場合に音声変換の負荷を外部にオフロードすることができます。
|
||||
|
||||

|
||||
|
||||
3. 複数のプラットフォームに対応しています。
|
||||
## 複数プラットフォームに対応
|
||||
|
||||
- Windows, Mac(M1), Linux, Google Colab (MMVC のみ)
|
||||
Windows, Mac(M1), Linux, Google Colab
|
||||
|
||||
# 使用方法
|
||||
*1 Linuxはリポジトリをcloneしてお使いください。
|
||||
|
||||
大きく 2 つの方法でご利用できます。難易度順に次の通りです。
|
||||
## REST APIを提供
|
||||
|
||||
- 事前ビルド済みの Binary での利用
|
||||
- Docker や Anaconda など環境構築を行った上での利用
|
||||
各種プログラミング言語でクライアントを作成することができます。
|
||||
|
||||
本ソフトウェアや MMVC になじみの薄い方は上から徐々に慣れていくとよいと思います。
|
||||
また、curlなどのOSに組み込まれているHTTPクライアントを使って操作ができます。
|
||||
|
||||
## (1) 事前ビルド済みの Binary での利用
|
||||
## トラブルシュート
|
||||
|
||||
- 実行形式のバイナリをダウンロードして実行することができます。
|
||||
[通信編](tutorials/trouble_shoot_communication_ja.md)
|
||||
|
||||
- チュートリアルは[こちら](tutorials/tutorial_rvc_ja_latest.md)をご覧ください。([ネットワークのトラブルシュート](https://github.com/w-okada/voice-changer/blob/master/tutorials/trouble_shoot_communication_ja.md))
|
||||
|
||||
- [Google Colaboratory](https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb) で簡単にお試しいただけるようになりました。左上の Open in Colab のボタンから起動できます。
|
||||
|
||||
<img src="https://github.com/w-okada/voice-changer/assets/48346627/3f092e2d-6834-42f6-bbfd-7d389111604e" width="400" height="150">
|
||||
|
||||
- Windows 版と Mac 版を提供しています。
|
||||
|
||||
- Windows かつ Nvidia の GPU をご使用の方は、ONNX(cpu,cuda), PyTorch(cpu,cuda)をダウンロードしてください。
|
||||
- Windows かつ AMD/Intel の GPU をご使用の方は、ONNX(cpu,DirectML), PyTorch(cpu,cuda)をダウンロードしてください。AMD/Intel の GPU は onnx のモデルを使用する場合のみ有効になります。
|
||||
- いずれの GPU のサポート状況についても、PyTorch、Onnxruntime がサポートしている場合のみ有効になります。
|
||||
- Windows で GPU をご使用にならない方は、ONNX(cpu,cuda), PyTorch(cpu,cuda)をダウンロードしてください。
|
||||
|
||||
- Windows 版は、ダウンロードした zip ファイルを解凍して、`start_http.bat`を実行してください。
|
||||
|
||||
- Mac 版はダウンロードファイルを解凍したのちに、`startHttp.command`を実行してください。開発元を検証できない旨が示される場合は、再度コントロールキーを押してクリックして実行してください(or 右クリックから実行してください)。
|
||||
|
||||
- 初回起動時は各種データをダウンロードします。ダウンロードに時間がかかる可能性があります。ダウンロードが完了すると、ブラウザが立ち上がります。
|
||||
|
||||
- リモートから接続する場合は、`.bat`ファイル(win)、`.command`ファイル(mac)の http が https に置き換わっているものを使用してください。
|
||||
|
||||
- DDPS-SVC の encoder は hubert-soft のみ対応です。
|
||||
|
||||
- ダウンロードはこちらから。
|
||||
|
||||
| Version | OS | フレームワーク | link | サポート VC | サイズ |
|
||||
| ---------- | --- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------ |
|
||||
| v.1.5.3.14 | mac | ONNX(cpu), PyTorch(cpu,mps) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 797MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3240MB |
|
||||
| | win | ONNX(cpu,DirectML), PyTorch(cpu,cuda) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3125MB |
|
||||
| v.1.5.3.13 | mac | ONNX(cpu), PyTorch(cpu,mps) | [google](https://drive.google.com/uc?id=1nD1jsDhqKtRt1m6LYZW9Xx02guVTYECi&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 797MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [google](https://drive.google.com/uc?id=1MCqHpJEzqo17QmaTRsvg74KVh-4g8S1o&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3240MB |
|
||||
| | win | ONNX(cpu,DirectML), PyTorch(cpu,cuda) | [google](https://drive.google.com/uc?id=18p5O_DBTE7296Eh-j8oi-8R5tBKxcDVB&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3125MB |
|
||||
|
||||
(\*1) Google Drive からダウンロードできない方は[hugging_face](https://huggingface.co/wok000/vcclient000/tree/main)からダウンロードしてみてください
|
||||
(\*2) 開発者が AMD のグラフィックボードを持っていないので動作確認していません。onnxruntime-directml を同梱しただけのものです。
|
||||
(\*3) 解凍や起動が遅い場合、ウィルス対策ソフトのチェックが走っている可能性があります。ファイルやフォルダを対象外にして実行してみてください。(自己責任です)
|
||||
|
||||
## (2) Docker や Anaconda など環境構築を行った上での利用
|
||||
|
||||
本リポジトリをクローンして利用します。Windows では WSL2 の環境構築が必須になります。また、WSL2 上で Docker もしくは Anaconda などの仮想環境の構築が必要となります。Mac では Anaconda などの Python の仮想環境の構築が必要となります。事前準備が必要となりますが、多くの環境においてこの方法が一番高速で動きます。**<font color="red"> GPU が無くてもそこそこ新しい CPU であれば十分動く可能性があります </font>(下記のリアルタイム性の節を参照)**。
|
||||
|
||||
[WSL2 と Docker のインストールの解説動画](https://youtu.be/POo_Cg0eFMU)
|
||||
|
||||
[WSL2 と Anaconda のインストールの解説動画](https://youtu.be/fba9Zhsukqw)
|
||||
|
||||
Docker での実行は、[Docker を使用する](docker_vcclient/README.md)を参考にサーバを起動してください。
|
||||
|
||||
Anaconda の仮想環境上での実行は、[サーバ開発者向けのページ](README_dev_ja.md)を参考にサーバを起動してください。
|
||||
|
||||
# トラブルシュート
|
||||
|
||||
- [通信編](tutorials/trouble_shoot_communication_ja.md)
|
||||
|
||||
# リアルタイム性(MMVC)
|
||||
|
||||
GPU を使用するとほとんどタイムラグなく変換可能です。
|
||||
|
||||
https://twitter.com/DannadoriYellow/status/1613483372579545088?s=20&t=7CLD79h1F3dfKiTb7M8RUQ
|
||||
|
||||
CPU でも最近のであればそれなりの速度で変換可能。
|
||||
|
||||
https://twitter.com/DannadoriYellow/status/1613553862773997569?s=20&t=7CLD79h1F3dfKiTb7M8RUQ
|
||||
|
||||
古い CPU( i7-4770)だと、1000msec くらいかかってしまう。
|
||||
|
||||
# 開発者の署名について
|
||||
## 開発者の署名について
|
||||
|
||||
本ソフトウェアは開発元の署名しておりません。下記のように警告が出ますが、コントロールキーを押しながらアイコンをクリックすると実行できるようになります。これは Apple のセキュリティポリシーによるものです。実行は自己責任となります。
|
||||
|
||||

|
||||
|
||||
# Acknowledgments
|
||||
## Acknowledgments
|
||||
|
||||
- [立ちずんだもん素材](https://seiga.nicovideo.jp/seiga/im10792934)
|
||||
- [いらすとや](https://www.irasutoya.com/)
|
||||
- [つくよみちゃん](https://tyc.rei-yumesaki.net/)
|
||||
* [立ちずんだもん素材](https://seiga.nicovideo.jp/seiga/im10792934)
|
||||
* [いらすとや](https://www.irasutoya.com/)
|
||||
* [つくよみちゃん](https://tyc.rei-yumesaki.net/)
|
||||
|
||||
```
|
||||
本ソフトウェアの音声合成には、フリー素材キャラクター「つくよみちゃん」が無料公開している音声データを使用しています。
|
||||
@ -143,12 +113,12 @@ https://twitter.com/DannadoriYellow/status/1613553862773997569?s=20&t=7CLD79h1F3
|
||||
© Rei Yumesaki
|
||||
```
|
||||
|
||||
- [あみたろの声素材工房](https://amitaro.net/)
|
||||
- [れぷりかどーる](https://kikyohiroto1227.wixsite.com/kikoto-utau)
|
||||
* [あみたろの声素材工房](https://amitaro.net/)
|
||||
* [れぷりかどーる](https://kikyohiroto1227.wixsite.com/kikoto-utau)
|
||||
|
||||
# 利用規約
|
||||
## 利用規約
|
||||
|
||||
- リアルタイムボイスチェンジャーつくよみちゃんについては、つくよみちゃんコーパスの利用規約に準じ、次の目的で変換後の音声を使用することを禁止します。
|
||||
* リアルタイムボイスチェンジャーつくよみちゃんについては、つくよみちゃんコーパスの利用規約に準じ、次の目的で変換後の音声を使用することを禁止します。
|
||||
|
||||
```
|
||||
|
||||
@ -162,7 +132,7 @@ https://twitter.com/DannadoriYellow/status/1613553862773997569?s=20&t=7CLD79h1F3
|
||||
※鑑賞用の作品として配布・販売していただくことは問題ございません。
|
||||
```
|
||||
|
||||
- リアルタイムボイスチェンジャーあみたろについては、あみたろの声素材工房様の次の利用規約に準じます。詳細は[こちら](https://amitaro.net/voice/faq/#index_id6)です。
|
||||
* リアルタイムボイスチェンジャーあみたろについては、あみたろの声素材工房様の次の利用規約に準じます。詳細は[こちら](https://amitaro.net/voice/faq/#index_id6)
|
||||
|
||||
```
|
||||
あみたろの声素材やコーパス読み上げ音声を使って音声モデルを作ったり、ボイスチェンジャーや声質変換などを使用して、自分の声をあみたろの声に変換して使うのもOKです。
|
||||
@ -171,31 +141,8 @@ https://twitter.com/DannadoriYellow/status/1613553862773997569?s=20&t=7CLD79h1F3
|
||||
また、あみたろの声で話す内容は声素材の利用規約の範囲内のみとし、センシティブな発言などはしないでください。
|
||||
```
|
||||
|
||||
- リアルタイムボイスチェンジャー黄琴まひろについては、れぷりかどーるの利用規約に準じます。詳細は[こちら](https://kikyohiroto1227.wixsite.com/kikoto-utau/ter%EF%BD%8Ds-of-service)です。
|
||||
* リアルタイムボイスチェンジャー黄琴まひろについては、れぷりかどーるの利用規約に準じます。詳細は[こちら](https://kikyohiroto1227.wixsite.com/kikoto-utau/ter%EF%BD%8Ds-of-service)
|
||||
|
||||
# 免責事項
|
||||
## 免責事項
|
||||
|
||||
本ソフトウェアの使用または使用不能により生じたいかなる直接損害・間接損害・波及的損害・結果的損害 または特別損害についても、一切責任を負いません。
|
||||
|
||||
# (1) レコーダー(トレーニング用音声録音アプリ)
|
||||
|
||||
MMVC トレーニング用の音声を簡単に録音できるアプリです。
|
||||
Github Pages 上で実行できるため、ブラウザのみあれば様々なプラットフォームからご利用可能です。
|
||||
録音したデータは、ブラウザ上に保存されます。外部に漏れることはありません。
|
||||
|
||||
[録音アプリ on Github Pages](https://w-okada.github.io/voice-changer/)
|
||||
|
||||
[解説動画](https://youtu.be/s_GirFEGvaA)
|
||||
|
||||
# 過去バージョン
|
||||
|
||||
| Version | OS | フレームワーク | link | サポート VC | サイズ |
|
||||
| ---------- | --- | --------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------ |
|
||||
| v.1.5.2.9e | mac | ONNX(cpu), PyTorch(cpu,mps) | [normal](https://drive.google.com/uc?id=1W0d7I7619PcO7kjb1SPXp6MmH5Unvd78&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 796MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [normal](https://drive.google.com/uc?id=1tmTMJRRggS2Sb4goU-eHlRvUBR88RZDl&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, so-vits-svc 4.0v2, RVC, DDSP-SVC | 2872MB |
|
||||
| v.1.5.3.1 | mac | ONNX(cpu), PyTorch(cpu,mps) | [normal](https://drive.google.com/uc?id=1oswF72q_cQQeXhIn6W275qLnoBAmcrR_&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 796MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [normal](https://drive.google.com/uc?id=1AWjDhW4w2Uljp1-9P8YUJBZsIlnhkJX2&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, so-vits-svc 4.0v2, RVC, DDSP-SVC | 2872MB |
|
||||
|
||||
# For Contributor
|
||||
|
||||
このリポジトリは[CLA](https://raw.githubusercontent.com/w-okada/voice-changer/master/LICENSE-CLA)を設定しています。
|
||||
|
@ -1,6 +1,6 @@
|
||||
## For Developper
|
||||
|
||||
[Japanese](/README_dev_ja.md)
|
||||
[Japanese](/README_dev_ja.md) [Russian](/README_dev_ru.md)
|
||||
|
||||
## Prerequisit
|
||||
|
||||
|
122
README_dev_ko.md
Normal file
122
README_dev_ko.md
Normal file
@ -0,0 +1,122 @@
|
||||
## 개발자용
|
||||
|
||||
[English](/README_dev_en.md) [Korean](/README_dev_ko.md)
|
||||
|
||||
## 전제
|
||||
|
||||
- Linux(ubuntu, debian) or WSL2, (다른 리눅스 배포판과 Mac에서는 테스트하지 않았습니다)
|
||||
- Anaconda
|
||||
|
||||
## 준비
|
||||
|
||||
1. Anaconda 가상 환경을 작성한다
|
||||
|
||||
```
|
||||
$ conda create -n vcclient-dev python=3.10
|
||||
$ conda activate vcclient-dev
|
||||
```
|
||||
|
||||
2. 리포지토리를 클론한다
|
||||
|
||||
```
|
||||
$ git clone https://github.com/w-okada/voice-changer.git
|
||||
```
|
||||
|
||||
## 서버 개발자용
|
||||
|
||||
1. 모듈을 설치한다
|
||||
|
||||
```
|
||||
$ cd voice-changer/server
|
||||
$ pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. 서버를 구동한다
|
||||
|
||||
다음 명령어로 구동합니다. 여러 가중치에 대한 경로는 환경에 맞게 변경하세요.
|
||||
|
||||
```
|
||||
$ python3 MMVCServerSIO.py -p 18888 --https true \
|
||||
--content_vec_500 pretrain/checkpoint_best_legacy_500.pt \
|
||||
--content_vec_500_onnx pretrain/content_vec_500.onnx \
|
||||
--content_vec_500_onnx_on true \
|
||||
--hubert_base pretrain/hubert_base.pt \
|
||||
--hubert_base_jp pretrain/rinna_hubert_base_jp.pt \
|
||||
--hubert_soft pretrain/hubert/hubert-soft-0d54a1f4.pt \
|
||||
--nsf_hifigan pretrain/nsf_hifigan/model \
|
||||
--crepe_onnx_full pretrain/crepe_onnx_full.onnx \
|
||||
--crepe_onnx_tiny pretrain/crepe_onnx_tiny.onnx \
|
||||
--rmvpe pretrain/rmvpe.pt \
|
||||
--model_dir model_dir \
|
||||
--samples samples.json
|
||||
```
|
||||
|
||||
브라우저(Chrome에서만 지원)에서 접속하면 화면이 나옵니다.
|
||||
|
||||
2-1. 문제 해결법
|
||||
|
||||
(1) OSError: PortAudio library not found
|
||||
다음과 같은 메시지가 나올 경우에는 추가 라이브러리를 설치해야 합니다.
|
||||
|
||||
```
|
||||
OSError: PortAudio library not found
|
||||
```
|
||||
|
||||
ubuntu(wsl2)인 경우에는 아래 명령어로 설치할 수 있습니다.
|
||||
|
||||
```
|
||||
$ sudo apt-get install libportaudio2
|
||||
$ sudo apt-get install libasound-dev
|
||||
```
|
||||
|
||||
(2) 서버 구동이 안 되는데요?!
|
||||
|
||||
클라이언트는 자동으로 구동되지 않습니다. 브라우저를 실행하고 콘솔에 표시된 URL로 접속하세요.
|
||||
|
||||
(3) Could not load library libcudnn_cnn_infer.so.8
|
||||
WSL를 사용 중이라면 `Could not load library libcudnn_cnn_infer.so.8. Error: libcuda.so: cannot open shared object file: No such file or directory`라는 메시지가 나오는 경우가 있습니다.
|
||||
잘못된 경로가 원인인 경우가 많습니다. 아래와 같이 경로를 바꾸고 실행해 보세요.
|
||||
.bashrc 등 구동 스크립트에 추가해 두면 편리합니다.
|
||||
|
||||
```
|
||||
export LD_LIBRARY_PATH=/usr/lib/wsl/lib:$LD_LIBRARY_PATH
|
||||
```
|
||||
|
||||
- 참고
|
||||
- https://qiita.com/cacaoMath/items/811146342946cdde5b83
|
||||
- https://github.com/microsoft/WSL/issues/8587
|
||||
|
||||
3. 개발하세요
|
||||
|
||||
### Appendix
|
||||
|
||||
1. Win + Anaconda일 때 (not supported)
|
||||
|
||||
pytorch를 conda가 없으면 gpu를 인식하지 않을 수 있습니다.
|
||||
|
||||
```
|
||||
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia
|
||||
```
|
||||
|
||||
또한 추가로 아래 내용도 필요합니다.
|
||||
|
||||
```
|
||||
pip install chardet
|
||||
pip install numpy==1.24.0
|
||||
```
|
||||
|
||||
## 클라이언트 개발자용
|
||||
|
||||
1. 모듈을 설치하고 한번 빌드합니다
|
||||
|
||||
```
|
||||
cd client
|
||||
cd lib
|
||||
npm install
|
||||
npm run build:dev
|
||||
cd ../demo
|
||||
npm install
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
2. 개발하세요
|
124
README_dev_ru.md
Normal file
124
README_dev_ru.md
Normal file
@ -0,0 +1,124 @@
|
||||
Вот перевод файла `README_dev_en.md` на русский язык:
|
||||
|
||||
## Для разработчиков
|
||||
|
||||
[Японский](/README_dev_ja.md) [Английский](/README_dev_en.md)
|
||||
|
||||
## Требования
|
||||
|
||||
- Linux (Ubuntu, Debian) или WSL2 (другие дистрибуции Linux и Mac не тестировались)
|
||||
- Anaconda
|
||||
|
||||
## Подготовка
|
||||
|
||||
1. Создайте виртуальную среду Anaconda:
|
||||
|
||||
```
|
||||
$ conda create -n vcclient-dev python=3.10
|
||||
$ conda activate vcclient-dev
|
||||
```
|
||||
|
||||
2. Клонируйте репозиторий:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/w-okada/voice-changer.git
|
||||
```
|
||||
|
||||
## Для серверных разработчиков
|
||||
|
||||
1. Установите необходимые зависимости:
|
||||
|
||||
```
|
||||
$ cd voice-changer/server
|
||||
$ pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. Запустите сервер
|
||||
|
||||
Запустите сервер с помощью следующей команды. Вы можете указать свои пути к весам моделей.
|
||||
|
||||
```
|
||||
$ python3 MMVCServerSIO.py -p 18888 --https true \
|
||||
--content_vec_500 pretrain/checkpoint_best_legacy_500.pt \
|
||||
--content_vec_500_onnx pretrain/content_vec_500.onnx \
|
||||
--content_vec_500_onnx_on true \
|
||||
--hubert_base pretrain/hubert_base.pt \
|
||||
--hubert_base_jp pretrain/rinna_hubert_base_jp.pt \
|
||||
--hubert_soft pretrain/hubert/hubert-soft-0d54a1f4.pt \
|
||||
--nsf_hifigan pretrain/nsf_hifigan/model \
|
||||
--crepe_onnx_full pretrain/crepe_onnx_full.onnx \
|
||||
--crepe_onnx_tiny pretrain/crepe_onnx_tiny.onnx \
|
||||
--rmvpe pretrain/rmvpe.pt \
|
||||
--model_dir model_dir \
|
||||
--samples samples.json
|
||||
```
|
||||
|
||||
Откройте браузер (на данный момент поддерживается только Chrome), и вы увидите графический интерфейс.
|
||||
|
||||
2-1. Устранение неполадок
|
||||
|
||||
(1) OSError: не найдена библиотека PortAudio
|
||||
|
||||
Если вы получите сообщение ниже, необходимо установить дополнительную библиотеку:
|
||||
|
||||
```
|
||||
OSError: PortAudio library not found
|
||||
```
|
||||
|
||||
Вы можете установить библиотеку командой:
|
||||
|
||||
```
|
||||
$ sudo apt-get install libportaudio2
|
||||
$ sudo apt-get install libasound-dev
|
||||
```
|
||||
|
||||
(2) Не запускается! Чертова программа!
|
||||
|
||||
Клиент не запускается автоматически. Пожалуйста, откройте браузер и перейдите по URL, отображаемому в консоли. И будьте осторожны со словами.
|
||||
|
||||
(3) Не удалось загрузить библиотеку libcudnn_cnn_infer.so.8
|
||||
|
||||
При использовании WSL может возникнуть ошибка `Could not load library libcudnn_cnn_infer.so.8. Error: libcuda.so: cannot open shared object file: No such file or directory`. Это часто связано с тем, что путь к библиотеке не установлен. Установите путь с помощью команды ниже. Вы можете добавить эту команду в ваш скрипт запуска, например, в .bashrc.
|
||||
|
||||
```
|
||||
export LD_LIBRARY_PATH=/usr/lib/wsl/lib:$LD_LIBRARY_PATH
|
||||
```
|
||||
|
||||
- ссылки:
|
||||
- https://qiita.com/cacaoMath/items/811146342946cdde5b83
|
||||
- https://github.com/microsoft/WSL/issues/8587
|
||||
|
||||
3. Наслаждайтесь разработкой.
|
||||
|
||||
### Приложение
|
||||
|
||||
1. Windows + Anaconda (не поддерживается)
|
||||
|
||||
Используйте conda для установки PyTorch:
|
||||
|
||||
```
|
||||
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia
|
||||
```
|
||||
|
||||
Также выполните эти команды:
|
||||
|
||||
```
|
||||
pip install chardet
|
||||
pip install numpy==1.24.0
|
||||
```
|
||||
|
||||
## Для клиентских разработчиков
|
||||
|
||||
1. Импорт модулей и начальная сборка:
|
||||
|
||||
```
|
||||
cd client
|
||||
cd lib
|
||||
npm install
|
||||
npm run build:dev
|
||||
cd ../demo
|
||||
npm install
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
2. Наслаждайтесь.
|
99
README_en.md
99
README_en.md
@ -1,49 +1,53 @@
|
||||
## VC Client
|
||||
|
||||
[Japanese](/README_ja.md)
|
||||
[Japanese](/README_ja.md) [Korean](/README_ko.md) [Russian](/README_ru.md)
|
||||
|
||||
## What's New!
|
||||
- v.1.5.3.14
|
||||
- Improve:
|
||||
- onnx performance (need to be converted)
|
||||
- Some fixes:
|
||||
- change default f0 det to onnx_rmvpe
|
||||
- disable unrecommnded f0 det on direct ml
|
||||
- Experimental
|
||||
- Add 16k RVC Sample (experimental)
|
||||
|
||||
- v.1.5.3.13
|
||||
|
||||
- Improve:
|
||||
- overwrite models when upload new model
|
||||
- Skip pass through confirm dialog
|
||||
- some bugfixes:
|
||||
|
||||
- v.1.5.3.12a
|
||||
|
||||
- Improve:
|
||||
- Pass through confirm dialog
|
||||
- We have released a sister product, the Text To Speech client.
|
||||
- You can enjoy voice generation with a simple interface.
|
||||
- For more details, click [here](https://github.com/w-okada/ttsclient).
|
||||
- Beatrice V2 Training Code Released!!!
|
||||
- [Training Code Repository](https://huggingface.co/fierce-cats/beatrice-trainer)
|
||||
- [Colab Version](https://github.com/w-okada/beatrice-trainer-colab)
|
||||
- v.2.0.70-beta (only for m1 mac)
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature:
|
||||
- The M1 Mac version of VCClient now supports Beatrice v2 beta.1.
|
||||
- v.2.0.69-beta (only for win)
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- bugfix:
|
||||
- server mode: change chunk size causes tune change
|
||||
- show blank icon
|
||||
- Fixed a bug where the start button would not be displayed in case of some exceptions
|
||||
- Adjusted the output buffer for server device mode
|
||||
- Fixed a bug where the sampling rate would change when settings were modified while using server device mode
|
||||
- Fixed a bug when using Japanese hubert
|
||||
- misc:
|
||||
- Added host API filter (highlighted) for server device mode
|
||||
- v.2.0.65-beta
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature: We have supported Beatrice v2 beta.1, enabling even higher quality voice conversion.
|
||||
|
||||
# What is VC Client
|
||||
|
||||
1. This is a client software for performing real-time voice conversion using various Voice Conversion (VC) AI. The supported AI for voice conversion are as follows.
|
||||
|
||||
- [MMVC](https://github.com/isletennos/MMVC_Trainer)
|
||||
- [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc)
|
||||
- [MMVC](https://github.com/isletennos/MMVC_Trainer) (only v1)
|
||||
- [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc) (only v1)
|
||||
- [RVC(Retrieval-based-Voice-Conversion)](https://github.com/liujing04/Retrieval-based-Voice-Conversion-WebUI)
|
||||
- [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC)
|
||||
- [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC) (only v1)
|
||||
- [Beatrice JVS Corpus Edition](https://prj-beatrice.com/) * experimental, (***NOT MIT License*** see [readme](https://github.com/w-okada/voice-changer/blob/master/server/voice_changer/Beatrice/)) * Only for Windows, CPU dependent (only v1)
|
||||
- [Beatrice v2](https://prj-beatrice.com/) (only for v2)
|
||||
|
||||
2. Distribute the load by running Voice Changer on a different PC
|
||||
1. Distribute the load by running Voice Changer on a different PC
|
||||
The real-time voice changer of this application works on a server-client configuration. By running the MMVC server on a separate PC, you can run it while minimizing the impact on other resource-intensive processes such as gaming commentary.
|
||||
|
||||

|
||||
|
||||
3. Cross-platform compatibility
|
||||
Supports Windows, Mac (including Apple Silicon M1), Linux, and Google Colaboratory.
|
||||
|
||||
## Related Software
|
||||
- [Real-time Voice Changer VCClient](https://github.com/w-okada/voice-changer)
|
||||
- [Text-to-Speech Software TTSClient](https://github.com/w-okada/ttsclient)
|
||||
- [Real-Time Speech Recognition Software ASRClient](https://github.com/w-okada/asrclient)
|
||||
# usage
|
||||
|
||||
This is an app for performing voice changes with MMVC and so-vits-svc.
|
||||
@ -57,14 +61,19 @@ It can be used in two main ways, in order of difficulty:
|
||||
|
||||
- You can download and run executable binaries.
|
||||
|
||||
- Please see [here](tutorials/tutorial_rvc_en_latest.md) for the tutorial. ([troubule shoot](https://github.com/w-okada/voice-changer/blob/master/tutorials/trouble_shoot_communication_ja.md))
|
||||
- Please see [here](tutorials/tutorial_rvc_en_latest.md) for the tutorial. ([trouble shoot](https://github.com/w-okada/voice-changer/blob/master/tutorials/trouble_shoot_communication_ja.md))
|
||||
|
||||
- It's now easy to try it out on [Google Colaboratory](https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb) (requires a ngrok account). You can launch it from the 'Open in Colab' button in the top left corner.
|
||||
- It's now easy to try it out on [Google Colaboratory](https://github.com/w-okada/voice-changer/tree/v.2/w_okada's_Voice_Changer_version_2_x.ipynb) (requires a ngrok account). You can launch it from the 'Open in Colab' button in the top left corner.
|
||||
|
||||
<img src="https://github.com/w-okada/voice-changer/assets/48346627/3f092e2d-6834-42f6-bbfd-7d389111604e" width="400" height="150">
|
||||
|
||||
- We offer Windows and Mac versions.
|
||||
|
||||
- We offer Windows and Mac versions on [hugging face](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
- v2 for Windows
|
||||
- Please download and use `vcclient_win_std_xxx.zip`. You can perform voice conversion using a reasonably high-performance CPU without a GPU, or by utilizing DirectML to leverage GPUs (AMD, Nvidia). v2 supports both torch and onnx.
|
||||
- If you have an Nvidia GPU, you can achieve faster voice conversion by using `vcclient_win_cuda_xxx.zip`.
|
||||
- v2 for Mac (Apple Silicon)
|
||||
- Please download and use `vcclient_mac_xxx.zip`.
|
||||
- v1
|
||||
- If you are using a Windows and Nvidia GPU, please download ONNX (cpu, cuda), PyTorch (cpu, cuda).
|
||||
- If you are using a Windows and AMD/Intel GPU, please download ONNX (cpu, DirectML) and PyTorch (cpu, cuda). AMD/Intel GPUs are only enabled for ONNX models.
|
||||
- In either case, for GPU support, PyTorch and Onnxruntime are only enabled if supported.
|
||||
@ -78,20 +87,7 @@ It can be used in two main ways, in order of difficulty:
|
||||
|
||||
- The encoder of DDPS-SVC only supports hubert-soft.
|
||||
|
||||
- Download (When you cannot download from google drive, try [hugging_face](https://huggingface.co/wok000/vcclient000/tree/main))
|
||||
|
||||
| Version | OS | フレームワーク | link | サポート VC | サイズ |
|
||||
| ---------- | --- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------ |
|
||||
| v.1.5.3.14 | mac | ONNX(cpu), PyTorch(cpu,mps) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 797MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3240MB |
|
||||
| | win | ONNX(cpu,DirectML), PyTorch(cpu,cuda) | [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3125MB |
|
||||
| v.1.5.3.13 | mac | ONNX(cpu), PyTorch(cpu,mps) | [google](https://drive.google.com/uc?id=1nD1jsDhqKtRt1m6LYZW9Xx02guVTYECi&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 797MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [google](https://drive.google.com/uc?id=1MCqHpJEzqo17QmaTRsvg74KVh-4g8S1o&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3240MB |
|
||||
| | win | ONNX(cpu,DirectML), PyTorch(cpu,cuda) | [google](https://drive.google.com/uc?id=18p5O_DBTE7296Eh-j8oi-8R5tBKxcDVB&export=download), [hugging face](https://huggingface.co/wok000/vcclient000/tree/main) | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC, DDSP-SVC, Diffusion-SVC | 3125MB |
|
||||
|
||||
(\*1) You can also download from [hugging_face](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
(\*2) The developer does not have an AMD graphics card, so it has not been tested. This package only includes onnxruntime-directml.
|
||||
(\*3) If unpacking or starting is slow, there is a possibility that virus checking is running on your antivirus software. Please try running it with the file or folder excluded from the target. (At your own risk)
|
||||
- [Download from hugging face](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
|
||||
## (2) Usage after setting up the environment such as Docker or Anaconda
|
||||
|
||||
@ -105,17 +101,8 @@ To run docker, see [start docker](docker_vcclient/README_en.md).
|
||||
|
||||
To run on Anaconda venv, see [server developer's guide](README_dev_en.md)
|
||||
|
||||
# Real-time performance
|
||||
To run on Linux using an AMD GPU, see [setup guide linux](tutorials/tutorial_anaconda_amd_rocm.md)
|
||||
|
||||
Conversion is almost instantaneous when using GPU.
|
||||
|
||||
https://twitter.com/DannadoriYellow/status/1613483372579545088?s=20&t=7CLD79h1F3dfKiTb7M8RUQ
|
||||
|
||||
Even with CPU, recent ones can perform conversions at a reasonable speed.
|
||||
|
||||
https://twitter.com/DannadoriYellow/status/1613553862773997569?s=20&t=7CLD79h1F3dfKiTb7M8RUQ
|
||||
|
||||
With an old CPU (i7-4770), it takes about 1000 msec for conversion.
|
||||
|
||||
# Software Signing
|
||||
|
||||
|
185
README_ko.md
Normal file
185
README_ko.md
Normal file
@ -0,0 +1,185 @@
|
||||
## VC Client
|
||||
|
||||
[English](/README_en.md) [Korean](/README_ko.md)
|
||||
|
||||
## 새로운 기능!
|
||||
- 자매품으로 텍스트 음성 변환 클라이언트를 출시하였습니다.
|
||||
- 간단한 인터페이스로 음성 생성을 즐길 수 있습니다.
|
||||
- 자세한 내용은 [여기](https://github.com/w-okada/ttsclient)를 참조하세요.
|
||||
- Beatrice V2 훈련 코드 공개!!!
|
||||
- [훈련 코드 리포지토리](https://huggingface.co/fierce-cats/beatrice-trainer)
|
||||
- [Colab 버전](https://github.com/w-okada/beatrice-trainer-colab)
|
||||
- v.2.0.70-beta (only for m1 mac)
|
||||
- [여기를 참조하십시오](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature:
|
||||
- M1 Mac 버전 VCClient에서도 Beatrice v2 beta.1을 지원합니다.
|
||||
- v.2.0.69-beta (only for win)
|
||||
- [여기를 참조하십시오](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- 버그 수정:
|
||||
- 일부 예외 발생 시 시작 버튼이 표시되지 않는 버그를 수정
|
||||
- 서버 디바이스 모드의 출력 버퍼 조정
|
||||
- 서버 디바이스 모드 사용 중 설정 변경 시 샘플링 레이트가 변하는 버그 수정
|
||||
- 일본어 hubert 사용 시 버그 수정
|
||||
- 기타:
|
||||
- 서버 디바이스 모드에 호스트 API 필터 추가 (강조 표시)
|
||||
- v.2.0.65-beta
|
||||
- [여기를 참조하십시오](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature: Beatrice v2 beta.1를 지원하여 더 높은 품질의 음성 변환이 가능해졌습니다
|
||||
|
||||
# VC Client란
|
||||
|
||||
1. 각종 음성 변환 AI(VC, Voice Conversion)를 활용해 실시간 음성 변환을 하기 위한 클라이언트 소프트웨어입니다. 지원하는 음성 변환 AI는 다음과 같습니다.
|
||||
|
||||
- 지원하는 음성 변환 AI (지원 VC)
|
||||
- [MMVC](https://github.com/isletennos/MMVC_Trainer) (only v1)
|
||||
- [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc) (only v1)
|
||||
- [RVC(Retrieval-based-Voice-Conversion)](https://github.com/liujing04/Retrieval-based-Voice-Conversion-WebUI)
|
||||
- [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC) (only v1)
|
||||
- [Beatrice JVS Corpus Edition](https://prj-beatrice.com/) * experimental, (***NOT MIT License*** see [readme](https://github.com/w-okada/voice-changer/blob/master/server/voice_changer/Beatrice/)) * Only for Windows, CPU dependent (only v1)
|
||||
- [Beatrice v2](https://prj-beatrice.com/) (only for v2)
|
||||
-
|
||||
1. 이 소프트웨어는 네트워크를 통한 사용도 가능하며, 게임 등 부하가 큰 애플리케이션과 동시에 사용할 경우 음성 변화 처리의 부하를 외부로 돌릴 수도 있습니다.
|
||||
|
||||

|
||||
|
||||
3. 여러 플랫폼을 지원합니다.
|
||||
|
||||
- Windows, Mac(M1), Linux, Google Colab (MMVC만 지원)
|
||||
## 관련 소프트웨어
|
||||
- [실시간 음성 변조기 VCClient](https://github.com/w-okada/voice-changer)
|
||||
- [텍스트 읽기 소프트웨어 TTSClient](https://github.com/w-okada/ttsclient)
|
||||
- [실시간 음성 인식 소프트웨어 ASRClient](https://github.com/w-okada/asrclient)
|
||||
# 사용 방법
|
||||
|
||||
크게 두 가지 방법으로 사용할 수 있습니다. 난이도 순서는 다음과 같습니다.
|
||||
|
||||
- 사전 빌드된 Binary 사용
|
||||
- Docker, Anaconda 등으로 구축된 개발 환경에서 사용
|
||||
|
||||
이 소프트웨어나 MMVC에 익숙하지 않은 분들은 위에서부터 차근차근 익숙해지길 추천합니다.
|
||||
|
||||
## (1) 사전 빌드된 Binary(파일) 사용
|
||||
|
||||
- 실행 형식 바이너리를 다운로드하여 실행할 수 있습니다.
|
||||
|
||||
- 튜토리얼은 [이곳](tutorials/tutorial_rvc_ko_latest.md)을 확인하세요。([네트워크 문제 해결법](https://github.com/w-okada/voice-changer/blob/master/tutorials/trouble_shoot_communication_ko.md))
|
||||
|
||||
- [Google Colaboratory](https://github.com/w-okada/voice-changer/tree/v.2/w_okada's_Voice_Changer_version_2_x.ipynb) で簡単にお試しいただけるようになりました。左上の Open in Colab のボタンから起動できます。
|
||||
|
||||
<img src="https://github.com/w-okada/voice-changer/assets/48346627/3f092e2d-6834-42f6-bbfd-7d389111604e" width="400" height="150">
|
||||
|
||||
- Windows 버전과 Mac 버전을 제공하고 있습니다. [Hugging Face](https://huggingface.co/wok000/vcclient000/tree/main)에서 다운로드할 수 있습니다.
|
||||
- Windows용 v2
|
||||
- `vcclient_win_std_xxx.zip`를 다운로드하여 사용하세요. GPU를 사용하지 않고도 (어느 정도 고성능의) CPU를 사용한 음성 변환이나, DirectML을 사용해 GPU(AMD, Nvidia)를 활용한 음성 변환이 가능합니다. v2에서는 torch와 onnx 모두를 지원합니다.
|
||||
- Nvidia GPU를 가지고 계신 분들은 `vcclient_win_cuda_xxx.zip`를 사용하시면 더 빠른 음성 변환이 가능합니다.
|
||||
- Mac (Apple Silicon)용 v2
|
||||
- `vcclient_mac_xxx.zip`를 다운로드하여 사용하세요.
|
||||
- v1
|
||||
- Windows와 NVIDIA GPU를 사용하는 분은 ONNX(cpu, cuda), PyTorch(cpu, cuda)를 다운로드하세요.
|
||||
- Windows와 AMD/Intel GPU를 사용하는 분은 ONNX(cpu, DirectML), PyTorch(cpu, cuda)를 다운로드하세요 AMD/Intel GPU는 ONNX 모델을 사용할 때만 적용됩니다.
|
||||
- 그 외 GPU도 PyTorch, Onnxruntime가 지원할 경우에만 적용됩니다.
|
||||
- Windows에서 GPU를 사용하지 않는 분은 ONNX(cpu, cuda), PyTorch(cpu, cuda)를 다운로드하세요.
|
||||
|
||||
- Windows 버전은 다운로드한 zip 파일의 압축을 풀고 `start_http.bat`를 실행하세요.
|
||||
|
||||
- Mac 버전은 다운로드한 파일을 풀고 `startHttp.command`를 실행하세요. 확인되지 않은 개발자 메시지가 나오면 다시 control 키를 누르고 클릭해 실행하세요(or 오른쪽 클릭으로 실행하세요).
|
||||
|
||||
- 처음 실행할 때는 인터넷으로 여러 데이터를 다운로드합니다. 다운로드할 때 시간이 좀 걸릴 수 있습니다. 다운로드가 완료되면 브라우저가 실행됩니다.
|
||||
|
||||
- 원격으로 접속할 때는 http 대신 https `.bat` 파일(win)、`.command` 파일(mac)을 실행하세요.
|
||||
|
||||
- DDPS-SVC의 encoder는 hubert-soft만 지원합니다.
|
||||
|
||||
|
||||
## (2) Docker나 Anaconda 등으로 구축된 개발 환경에서 사용
|
||||
|
||||
이 리포지토리를 클론해 사용할 수 있습니다. Windows에서는 WSL2 환경 구축이 필수입니다. 또한, WSL2 상에 Docker나 Anaconda 등의 가상환경 구축이 필요합니다. Mac에서는 Anaconda 등의 Python 가상환경 구축이 필요합니다. 사전 준비가 필요하지만, 많은 환경에서 이 방법이 가장 빠르게 작동합니다. **<font color="red"> GPU가 없어도 나름 최근 출시된 CPU가 있다면 충분히 작동할 가능성이 있습니다</font>(아래 실시간성 항목 참조)**.
|
||||
|
||||
[WSL2와 Docker 설치 설명 영상](https://youtu.be/POo_Cg0eFMU)
|
||||
|
||||
[WSL2와 Anaconda 설치 설명 영상](https://youtu.be/fba9Zhsukqw)
|
||||
|
||||
Docker에서 실행은 [Docker를 사용](docker_vcclient/README_ko.md)을 참고해 서버를 구동하세요.
|
||||
|
||||
Anaconda 가상 환경에서 실행은 [서버 개발자용 문서](README_dev_ko.md)를 참고해 서버를 구동하세요.
|
||||
|
||||
# 문제 해결법
|
||||
|
||||
- [통신편](tutorials/trouble_shoot_communication_ko.md)
|
||||
|
||||
|
||||
# 개발자 서명에 대하여
|
||||
|
||||
이 소프트웨어는 개발자 서명이 없습니다. 本ソフトウェアは開発元の署名しておりません。下記のように警告が出ますが、コントロールキーを押しながらアイコンをクリックすると実行できるようになります。これは Apple のセキュリティポリシーによるものです。実行は自己責任となります。
|
||||
|
||||

|
||||
(이미지 번역: ctrl을 누른 채로 클릭)
|
||||
|
||||
# 감사의 말
|
||||
|
||||
- [立ちずんだもん素材](https://seiga.nicovideo.jp/seiga/im10792934)
|
||||
- [いらすとや](https://www.irasutoya.com/)
|
||||
- [つくよみちゃん](https://tyc.rei-yumesaki.net/)
|
||||
|
||||
```
|
||||
이 소프트웨어의 음성 합성에는 무료 소재 캐릭터 「つくよみちゃん(츠쿠요미 짱)」이 무료 공개하고 있는 음성 데이터를 사용했습니다.■츠쿠요미 짱 말뭉치(CV.夢前黎)
|
||||
https://tyc.rei-yumesaki.net/material/corpus/
|
||||
© Rei Yumesaki
|
||||
```
|
||||
|
||||
- [あみたろの声素材工房](https://amitaro.net/)
|
||||
- [れぷりかどーる](https://kikyohiroto1227.wixsite.com/kikoto-utau)
|
||||
|
||||
# 이용약관
|
||||
|
||||
- 실시간 음성 변환기 츠쿠요미 짱은 츠쿠요미 짱 말뭉치 이용약관에 따라 다음과 같은 목적으로 변환 후 음성을 사용하는 것을 금지합니다.
|
||||
|
||||
```
|
||||
|
||||
■사람을 비판·공격하는 행위. ("비판·공격"의 정의는 츠쿠요미 짱 캐릭터 라이센스에 준합니다)
|
||||
|
||||
■특정 정치적 입장·종교·사상에 대한 찬반을 논하는 행위.
|
||||
|
||||
■자극적인 표현물을 무분별하게 공개하는 행위.
|
||||
|
||||
■타인에게 2차 창작(소재로서의 활용)을 허가하는 형태로 공개하는 행위.
|
||||
※감상용 작품으로서 배포·판매하는 건 문제없습니다.
|
||||
```
|
||||
|
||||
- 실시간 음성 변환기 아미타로는 あみたろの声素材工房(아미타로의 음성 소재 공방)의 다음 이용약관에 따릅니다. 자세한 내용은 [이곳](https://amitaro.net/voice/faq/#index_id6)에 있습니다.
|
||||
|
||||
```
|
||||
아미타로의 음성 소재나 말뭉치 음성으로 음성 모델을 만들거나, 음성 변환기나 말투 변환기 등을 사용해 본인 목소리를 아미타로의 목소리로 변환해 사용하는 것도 괜찮습니다.
|
||||
|
||||
단, 그 경우에는 반드시 아미타로(혹은 코하루네 아미)의 음성으로 변환한 것을 명시하고, 아미타로(및 코하루네 아미)가 말하는 것이 아님을 누구나 알 수 있도록 하십시오.
|
||||
또한 아미타로의 음성으로 말하는 내용은 음성 소재 이용약관의 범위 내에서만 사용해야 하며, 민감한 발언은 삼가십시오.
|
||||
```
|
||||
|
||||
- 실시간 음성 변환기 키코토 마히로는 れぷりかどーる(레플리카 돌)의 이용약관에 따릅니다. 자세한 내용은 [이곳](https://kikyohiroto1227.wixsite.com/kikoto-utau/ter%EF%BD%8Ds-of-service)에 있습니다.
|
||||
|
||||
# 면책 사항
|
||||
|
||||
이 소프트웨어의 사용 또는 사용 불능으로 인해 발생한 직접 손해·간접 손해·파생적 손해·결과적 손해 또는 특별 손해에 대해 모든 책임을 지지 않습니다.
|
||||
|
||||
# (1) 레코더(트레이닝용 음성 녹음 앱)
|
||||
|
||||
MMVC 트레이닝용 음성을 간단하게 녹음할 수 있는 앱입니다.
|
||||
Github Pages에서 실행할 수 있어서 브라우저만 있으면 다양한 플랫폼에서 사용할 수 있습니다.
|
||||
녹음한 데이터는 브라우저에 저장됩니다. 외부로 유출되지 않습니다.
|
||||
|
||||
[녹음 앱 on Github Pages](https://w-okada.github.io/voice-changer/)
|
||||
|
||||
[설명 영상](https://youtu.be/s_GirFEGvaA)
|
||||
|
||||
# 이전 버전
|
||||
|
||||
| Version | OS | 프레임워크 | link | 지원 VC | 파일 크기 |
|
||||
| ---------- | --- | --------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------- |
|
||||
| v.1.5.2.9e | mac | ONNX(cpu), PyTorch(cpu,mps) | [normal](https://drive.google.com/uc?id=1W0d7I7619PcO7kjb1SPXp6MmH5Unvd78&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 796MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [normal](https://drive.google.com/uc?id=1tmTMJRRggS2Sb4goU-eHlRvUBR88RZDl&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, so-vits-svc 4.0v2, RVC, DDSP-SVC | 2872MB |
|
||||
| v.1.5.3.1 | mac | ONNX(cpu), PyTorch(cpu,mps) | [normal](https://drive.google.com/uc?id=1oswF72q_cQQeXhIn6W275qLnoBAmcrR_&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, RVC | 796MB |
|
||||
| | win | ONNX(cpu,cuda), PyTorch(cpu,cuda) | [normal](https://drive.google.com/uc?id=1AWjDhW4w2Uljp1-9P8YUJBZsIlnhkJX2&export=download) \*1 | MMVC v.1.5.x, MMVC v.1.3.x, so-vits-svc 4.0, so-vits-svc 4.0v2, RVC, DDSP-SVC | 2872MB |
|
||||
|
||||
# For Contributor
|
||||
|
||||
이 리포지토리는 [CLA](https://raw.githubusercontent.com/w-okada/voice-changer/master/LICENSE-CLA)를 설정했습니다.
|
119
README_ru.md
Normal file
119
README_ru.md
Normal file
@ -0,0 +1,119 @@
|
||||
[Японский](/README_ja.md) [Корейский](/README_ko.md) [Английский](/README_en.md)
|
||||
|
||||
## Что нового!
|
||||
- Мы выпустили продукт-сестру - клиент Text To Speech.
|
||||
- Вы можете насладиться генерацией голоса через простой интерфейс.
|
||||
- Подробнее [здесь](https://github.com/w-okada/ttsclient).
|
||||
- Код тренировки Beatrice V2 теперь доступен!
|
||||
- [Репозиторий кода тренировки](https://huggingface.co/fierce-cats/beatrice-trainer)
|
||||
- [Версия для Colab](https://github.com/w-okada/beatrice-trainer-colab)
|
||||
- v.2.0.70-beta (only for m1 mac)
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature:
|
||||
- В версии VCClient для Mac на базе M1 теперь поддерживается Beatrice v2 beta.1.
|
||||
- v.2.0.69-beta (only for win)
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- Исправления ошибок:
|
||||
- Исправлена ошибка, из-за которой кнопка запуска не отображалась в случае некоторых исключений
|
||||
- Настроен выходной буфер для режима серверного устройства
|
||||
- Исправлена ошибка, при которой изменялась частота дискретизации при изменении настроек в режиме серверного устройства
|
||||
- Исправлена ошибка при использовании японского hubert
|
||||
- Прочее:
|
||||
- Добавлен фильтр API хоста (выделено) для режима серверного устройства
|
||||
- v.2.0.65-beta
|
||||
- [HERE](https://github.com/w-okada/voice-changer/tree/v.2)
|
||||
- new feature: We have supported Beatrice v2 beta.1, enabling even higher quality voice conversion.
|
||||
|
||||
# Что такое VC Клиент
|
||||
|
||||
1. Это клиентское ПО для выполнения преобразования голоса в реальном времени с использованием различных AI для преобразования голоса. Поддерживаемые AI:
|
||||
- [MMVC](https://github.com/isletennos/MMVC_Trainer) (только v1)
|
||||
- [so-vits-svc](https://github.com/svc-develop-team/so-vits-svc) (только v1)
|
||||
- [RVC (Retrieval-based Voice Conversion)](https://github.com/liujing04/Retrieval-based-Voice-Conversion-WebUI)
|
||||
- [DDSP-SVC](https://github.com/yxlllc/DDSP-SVC) (только v1)
|
||||
- [Beatrice JVS Corpus Edition](https://prj-beatrice.com/) * экспериментальный * (не по лицензии MIT, см. [readme](https://github.com/w-okada/voice-changer/blob/master/server/voice_changer/Beatrice/)), только для Windows, зависит от процессора (только v1)
|
||||
- [Beatrice v2](https://prj-beatrice.com/) (только v2)
|
||||
|
||||
2. Распределение нагрузки между разными ПК
|
||||
Реализация преобразования голоса работает по схеме "сервер-клиент". Вы можете запустить сервер MMVC на отдельном ПК для минимизации влияния на другие ресурсоёмкие процессы, такие как стриминг.
|
||||
|
||||

|
||||
|
||||
3. Кроссплатформенная совместимость
|
||||
Поддержка Windows, Mac (включая Apple Silicon M1), Linux и Google Colaboratory.
|
||||
|
||||
# Как использовать
|
||||
|
||||
Это приложение для изменения голоса с использованием MMVC и so-vits-svc.
|
||||
|
||||
Есть два основных способа использования, в порядке сложности:
|
||||
|
||||
- Использование готового исполняемого файла
|
||||
- Настройка окружения с Docker или Anaconda
|
||||
|
||||
## (1) Использование готовых исполняемых файлов
|
||||
|
||||
- Вы можете скачать и запустить исполняемые файлы.
|
||||
|
||||
- Смотрите [здесь](tutorials/tutorial_rvc_en_latest.md) для получения руководства. ([устранение неполадок](https://github.com/w-okada/voice-changer/blob/master/tutorials/trouble_shoot_communication_ja.md))
|
||||
|
||||
- Теперь попробовать можно на [Google Colaboratory](https://github.com/w-okada/voice-changer/tree/v.2/w_okada's_Voice_Changer_version_2_x.ipynb) (требуется аккаунт ngrok). Вы можете запустить его через кнопку "Открыть в Colab" в верхнем левом углу.
|
||||
|
||||
<img src="https://github.com/w-okada/voice-changer/assets/48346627/3f092e2d-6834-42f6-bbfd-7d389111604e" width="400" height="150">
|
||||
|
||||
- Мы предлагаем версии для Windows и Mac на [hugging face](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
- v2 для Windows
|
||||
- Пожалуйста, скачайте и используйте `vcclient_win_std_xxx.zip`. Преобразование голоса можно выполнять с использованием мощного процессора без GPU или с использованием DirectML для GPU (AMD, Nvidia). v2 поддерживает как torch, так и onnx.
|
||||
- Если у вас Nvidia GPU, скачайте `vcclient_win_cuda_xxx.zip` для более быстрого преобразования.
|
||||
- v2 для Mac (Apple Silicon)
|
||||
- Пожалуйста, скачайте и используйте `vcclient_mac_xxx.zip`.
|
||||
- v1
|
||||
- Для Windows с Nvidia GPU скачайте ONNX (cpu, cuda), PyTorch (cpu, cuda).
|
||||
- Для Windows с AMD/Intel GPU скачайте ONNX (cpu, DirectML) и PyTorch (cpu, cuda). AMD/Intel GPU поддерживаются только для ONNX моделей.
|
||||
|
||||
- Для пользователей Windows: после распаковки zip-файла запустите соответствующий `start_http.bat` файл.
|
||||
|
||||
- Для Mac: после распаковки zip-файла дважды щёлкните на `startHttp.command`. Если появится сообщение о невозможности проверки разработчика, нажмите Ctrl и повторно запустите.
|
||||
|
||||
- Если подключаетесь удалённо, используйте `.command` (Mac) или `.bat` (Windows) файл с https вместо http.
|
||||
|
||||
- Энкодер DDPS-SVC поддерживает только hubert-soft.
|
||||
|
||||
- [Скачать с hugging face](https://huggingface.co/wok000/vcclient000/tree/main)
|
||||
|
||||
## (2) Использование после настройки окружения с Docker или Anaconda
|
||||
|
||||
Клонируйте этот репозиторий и используйте его. Для Windows требуется настройка WSL2. Для Mac нужно настроить виртуальные среды Python, например Anaconda. Этот метод обеспечивает наивысшую скорость в большинстве случаев. **<font color="red"> Даже без GPU можно получить достаточную производительность на современном процессоре </font>(смотрите раздел о производительности в реальном времени ниже)**.
|
||||
|
||||
[Видео-инструкция по установке WSL2 и Docker](https://youtu.be/POo_Cg0eFMU)
|
||||
|
||||
[Видео-инструкция по установке WSL2 и Anaconda](https://youtu.be/fba9Zhsukqw)
|
||||
|
||||
Для запуска Docker смотрите [start docker](docker_vcclient/README_en.md).
|
||||
|
||||
Для запуска на Anaconda venv смотрите [руководство разработчика](README_dev_ru.md).
|
||||
|
||||
Для запуска на Linux с AMD GPU смотрите [руководство](tutorials/tutorial_anaconda_amd_rocm.md).
|
||||
|
||||
# Подпись программного обеспечения
|
||||
|
||||
Это ПО не подписано разработчиком. Появится предупреждение, но его можно запустить, нажав на иконку с удержанием клавиши Ctrl. Это связано с политикой безопасности Apple. Использование ПО на ваш риск.
|
||||
|
||||

|
||||
|
||||
https://user-images.githubusercontent.com/48346627/212569645-e30b7f4e-079d-4504-8cf8-7816c5f40b00.mp4
|
||||
|
||||
# Благодарности
|
||||
|
||||
- [Материалы Tachizunda-mon](https://seiga.nicovideo.jp/seiga/im10792934)
|
||||
- [Irasutoya](https://www.irasutoya.com/)
|
||||
- [Tsukuyomi-chan](https://tyc.rei-yumesaki.net)
|
||||
|
||||
> Это ПО использует голосовые данные бесплатного материала персонажа "Цукуёми-тян", предоставленного CV. Юмесаки Рэй.
|
||||
>
|
||||
> - Корпус Цукуёми-тян (CV. Юмесаки Рэй)
|
||||
>
|
||||
> https://tyc.rei-yumesaki.net/material/corpus/
|
||||
>
|
||||
> Авторское право. Юмесаки Рэй, Все права защищены.
|
||||
|
1595
SoftVcDemo.ipynb
1595
SoftVcDemo.ipynb
File diff suppressed because it is too large
Load Diff
@ -1,421 +0,0 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "VoiceChangerDemo",
|
||||
"provenance": [],
|
||||
"authorship_tag": "ABX9TyPFsamraQogYywSbvGGKbNM",
|
||||
"include_colab_link": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"accelerator": "GPU",
|
||||
"gpuClass": "standard"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view-in-github",
|
||||
"colab_type": "text"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/w-okada/voice-changer/blob/master/VoiceChangerDemo.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# このノートブックは管理されていません。\n",
|
||||
"# VCClientをColabで使用する場合は、次のノートブックをご使用ください。\n",
|
||||
"\n",
|
||||
"# This notebook is not being managed.\n",
|
||||
"# If you're using VCClient in Colab, please use the following notebook.\n",
|
||||
"\n",
|
||||
"https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Vm9wBO9-xWFP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"MMVCプレイヤー(普通版)\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"このノートはColab上でMMVCのボイチェンを行うノートです。\n",
|
||||
"\n",
|
||||
"正式版はローカルPC上で動かすアプリケーションです。\n",
|
||||
"\n",
|
||||
"正式版は、多くの場合より少ないタイムラグで滑らかに音声を変換できます。\n",
|
||||
"\n",
|
||||
"詳細な使用方法はこちらの[リポジトリ](https://github.com/w-okada/voice-changer)からご確認ください。\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Lbbmx_Vjl0zo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# GPUを確認\n",
|
||||
"GPUを用いたほうが高速に処理が行えます。\n",
|
||||
"\n",
|
||||
"下記のコマンドでGPUが確認できない場合は、上のメニューから\n",
|
||||
"\n",
|
||||
"「ランタイム」→「ランタイムの変更」→「ハードウェア アクセラレータ」\n",
|
||||
"\n",
|
||||
"でGPUを選択してください。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "oUKi1NYMmXrr"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (1) GPUの確認\n",
|
||||
"!nvidia-smi"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vV1t7PBRm-o6",
|
||||
"outputId": "de727091-7c9f-4dbc-a0cc-5985409b289f"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Sun Jan 29 12:09:35 2023 \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 510.47.03 Driver Version: 510.47.03 CUDA Version: 11.6 |\n",
|
||||
"|-------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|===============================+======================+======================|\n",
|
||||
"| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 47C P0 26W / 70W | 0MiB / 15360MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=============================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+-----------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# 使用するモデルとコンフィグファイルの指定\n",
|
||||
"\n",
|
||||
"使用するトレーニング済みのモデルと、トレーニングで使用したコンフィグファイルのパスを指定してください。\n",
|
||||
"\n",
|
||||
"多くの場合はGoogle Driveに格納されているファイルを使用すると思います。その場合は、下の(2-2)のセルを実行してドライブをマウントしてください"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "mHvGrgaWnIPA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (2-1) 使用するモデルとコンフィグファイルの指定\n",
|
||||
"if \"MODEL\" in locals():\n",
|
||||
" del MODEL\n",
|
||||
"if \"ONNX\" in locals():\n",
|
||||
" del ONNX\n",
|
||||
"\n",
|
||||
"CONFIG=\"/content/drive/MyDrive/VoiceChanger/config.json\"\n",
|
||||
"#MODEL=\"/content/drive/MyDrive/VoiceChanger/G_326000.pth\"\n",
|
||||
"ONNX=\"/content/drive/MyDrive/VoiceChanger/G_326000.onnx\""
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nSXATMWYb4Ik"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "2wxD-gRSMU5R",
|
||||
"outputId": "4c9da537-a5cb-4c5d-f999-2795b00d41a8"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Mounted at /content/drive\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# (2-2) Google Driveのマウント\n",
|
||||
"from google.colab import drive\n",
|
||||
"drive.mount('/content/drive')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# リポジトリのクローン\n",
|
||||
"リポジトリをクローンします"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "sLBfykjBnjWc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (3) リポジトリのクローン\n",
|
||||
"!git clone --depth 1 https://github.com/w-okada/voice-changer.git -b v.1.3.7\n",
|
||||
"%cd voice-changer/server\n",
|
||||
"!git clone https://github.com/isletennos/MMVC_Client.git\n",
|
||||
"!cd MMVC_Client && git checkout 04f3fec4fd82dea6657026ec4e1cd80fb29a415c && cd -"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "86wTFmqsNMnD",
|
||||
"outputId": "2b329892-41b2-4560-e4f0-ab49b4ef83bc"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Cloning into 'voice-changer'...\n",
|
||||
"remote: Enumerating objects: 157, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (157/157), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (141/141), done.\u001b[K\n",
|
||||
"remote: Total 157 (delta 21), reused 70 (delta 5), pack-reused 0\u001b[K\n",
|
||||
"Receiving objects: 100% (157/157), 1.61 MiB | 4.03 MiB/s, done.\n",
|
||||
"Resolving deltas: 100% (21/21), done.\n",
|
||||
"/content/voice-changer/server\n",
|
||||
"Cloning into 'MMVC_Client'...\n",
|
||||
"remote: Enumerating objects: 611, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (337/337), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (120/120), done.\u001b[K\n",
|
||||
"remote: Total 611 (delta 238), reused 285 (delta 214), pack-reused 274\u001b[K\n",
|
||||
"Receiving objects: 100% (611/611), 752.38 KiB | 21.50 MiB/s, done.\n",
|
||||
"Resolving deltas: 100% (360/360), done.\n",
|
||||
"Note: switching to '04f3fec4fd82dea6657026ec4e1cd80fb29a415c'.\n",
|
||||
"\n",
|
||||
"You are in 'detached HEAD' state. You can look around, make experimental\n",
|
||||
"changes and commit them, and you can discard any commits you make in this\n",
|
||||
"state without impacting any branches by switching back to a branch.\n",
|
||||
"\n",
|
||||
"If you want to create a new branch to retain commits you create, you may\n",
|
||||
"do so (now or later) by using -c with the switch command. Example:\n",
|
||||
"\n",
|
||||
" git switch -c <new-branch-name>\n",
|
||||
"\n",
|
||||
"Or undo this operation with:\n",
|
||||
"\n",
|
||||
" git switch -\n",
|
||||
"\n",
|
||||
"Turn off this advice by setting config variable advice.detachedHead to false\n",
|
||||
"\n",
|
||||
"HEAD is now at 04f3fec Merge pull request #30 from Mokuichi147/setupcheck\n",
|
||||
"/content/voice-changer/server\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# モジュールのインストール\n",
|
||||
"\n",
|
||||
"必要なモジュールをインストールします。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "8Na2PbLZSWgZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (5) 設定ファイルの確認\n",
|
||||
"!apt-get install -y libsndfile1-dev &> /dev/null\n",
|
||||
"!pip install fastapi &> /dev/null\n",
|
||||
"!pip install pyOpenSSL &> /dev/null\n",
|
||||
"!pip install python-multipart &> /dev/null\n",
|
||||
"!pip install python-socketio &> /dev/null\n",
|
||||
"!pip install uvicorn &> /dev/null\n",
|
||||
"!pip install websockets &> /dev/null\n",
|
||||
"!pip install onnxruntime-gpu &> /dev/null"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "LwZAAuqxX7yY"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# サーバの起動\n",
|
||||
"\n",
|
||||
"サーバを起動します。(6-1)\n",
|
||||
"\n",
|
||||
"サーバの起動状況を確認します。(6-2)\n",
|
||||
"\n",
|
||||
"このセルは繰り返し実行することになるのでCtrl+Retでセルを実行してください。\n",
|
||||
"\n",
|
||||
"アクセスできるようになるまで、1~2分かかるようです。コーヒーでも飲みに行きましょう。\n",
|
||||
"\n",
|
||||
"下記のようなテキストが表示されたら起動完了です。\n",
|
||||
"(warningは無視して問題ありません。)\n",
|
||||
"```\n",
|
||||
"/usr/local/lib/python3.8/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py:54: UserWarning: Specified provider 'OpenVINOExecutionProvider' is not in available provider names.Available providers: 'TensorrtExecutionProvider, CUDAExecutionProvider, CPUExecutionProvider'\n",
|
||||
" warnings.warn(\n",
|
||||
"/usr/local/lib/python3.8/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py:54: UserWarning: Specified provider 'DmlExecutionProvider' is not in available provider names.Available providers: 'TensorrtExecutionProvider, CUDAExecutionProvider, CPUExecutionProvider'\n",
|
||||
" warnings.warn(\n",
|
||||
"VoiceChanger Initialized (GPU_NUM:1, mps_enabled:False)\n",
|
||||
" Voice Changerを起動しています。\n",
|
||||
" -- 設定 --\n",
|
||||
" CONFIG:/content/drive/MyDrive/VoiceChanger/config.json, MODEL:None ONNX_MODEL:/content/drive/MyDrive/VoiceChanger/G_326000.onnx\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "-_2OcN9Borke"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (6-1) サーバの起動\n",
|
||||
"import random\n",
|
||||
"PORT = 10000 + random.randint(1, 9999)\n",
|
||||
"LOG_FILE = f\"LOG_FILE_{PORT}\"\n",
|
||||
"\n",
|
||||
"if \"MODEL\" in locals() and \"ONNX\" in locals():\n",
|
||||
" model_param = f\" -m {MODEL} -o {ONNX}\"\n",
|
||||
"elif \"MODEL\" in locals():\n",
|
||||
" model_param = f\" -m {MODEL}\"\n",
|
||||
"elif \"ONNX\" in locals():\n",
|
||||
" model_param = f\" -o {ONNX}\"\n",
|
||||
"else:\n",
|
||||
" model_param = f\"\"\n",
|
||||
"\n",
|
||||
"get_ipython().system_raw(f'python3 MMVCServerSIO.py -t MMVC -p {PORT} -c {CONFIG} {model_param} --https False --colab True >{LOG_FILE} 2>&1 &')\n",
|
||||
"#print(f\"PORT:{PORT}, LOG_FILE:{LOG_FILE}\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "iNOAB7zISI6J"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (6-2) サーバの起動確認 (Ctrl+Retで実行)\n",
|
||||
"!tail -20 {LOG_FILE}"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "chu06KpAjEK6",
|
||||
"outputId": "cf0d26f3-66a9-406e-e739-f588483d2851"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"/usr/local/lib/python3.8/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py:54: UserWarning: Specified provider 'OpenVINOExecutionProvider' is not in available provider names.Available providers: 'TensorrtExecutionProvider, CUDAExecutionProvider, CPUExecutionProvider'\n",
|
||||
" warnings.warn(\n",
|
||||
"/usr/local/lib/python3.8/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py:54: UserWarning: Specified provider 'DmlExecutionProvider' is not in available provider names.Available providers: 'TensorrtExecutionProvider, CUDAExecutionProvider, CPUExecutionProvider'\n",
|
||||
" warnings.warn(\n",
|
||||
"VoiceChanger Initialized (GPU_NUM:1, mps_enabled:False)\n",
|
||||
"\u001b[32m Voice Changerを起動しています。\u001b[0m\n",
|
||||
"\u001b[34m -- 設定 -- \u001b[0m\n",
|
||||
"\u001b[34m CONFIG:/content/drive/MyDrive/VoiceChanger/config.json, MODEL:None ONNX_MODEL:/content/drive/MyDrive/VoiceChanger/G_326000.onnx\u001b[0m\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# プロキシを起動\n",
|
||||
"ウェブサーバへのアクセスをするためのプロキシを起動します。\n",
|
||||
"\n",
|
||||
"表示されたURLをクリックして開くと別タブでアプリが開きます。\n",
|
||||
"\n",
|
||||
"Colabなので、ロードにある程度時間がかかります(30秒くらい)。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "WhxcFLQEpctq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (7) プロキシを起動\n",
|
||||
"from google.colab.output import eval_js\n",
|
||||
"proxy = eval_js( \"google.colab.kernel.proxyPort(\" + str(PORT) + \")\" )\n",
|
||||
"print(f\"{proxy}front/?colab=true\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nkRjZm95l87C",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 34
|
||||
},
|
||||
"outputId": "b6ef1db3-37b4-4025-fdbf-711e00646902"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"https://nafdn2dmorn-496ff2e9c6d22116-18341-colab.googleusercontent.com/front/?colab=true\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [],
|
||||
"metadata": {
|
||||
"id": "Jos5WZHGmz4s"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
@ -1,347 +0,0 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"authorship_tag": "ABX9TyPxc46bC1GMDee02W4h1fR8",
|
||||
"include_colab_link": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"accelerator": "GPU",
|
||||
"gpuClass": "standard"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view-in-github",
|
||||
"colab_type": "text"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/w-okada/voice-changer/blob/master/VoiceChangerDemo_Simple.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# このノートブックは管理されていません。\n",
|
||||
"# VCClientをColabで使用する場合は、次のノートブックをご使用ください。\n",
|
||||
"\n",
|
||||
"# This notebook is not being managed.\n",
|
||||
"# If you're using VCClient in Colab, please use the following notebook.\n",
|
||||
"\n",
|
||||
"https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "gWeB56-tyGJK"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"MMVCプレイヤー(超簡単版)\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"このノートはColab上でMMVCのボイチェンを行うノートです。\n",
|
||||
"\n",
|
||||
"正式版はローカルPC上で動かすアプリケーションです。\n",
|
||||
"\n",
|
||||
"正式版は、多くの場合より少ないタイムラグで滑らかに音声を変換できます。\n",
|
||||
"\n",
|
||||
"詳細な使用方法はこちらの[リポジトリ](https://github.com/w-okada/voice-changer)からご確認ください。\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Lbbmx_Vjl0zo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# GPUを確認\n",
|
||||
"GPUを用いたほうが高速に処理が行えます。\n",
|
||||
"\n",
|
||||
"下記のコマンドでGPUが確認できない場合は、上のメニューから\n",
|
||||
"\n",
|
||||
"「ランタイム」→「ランタイムの変更」→「ハードウェア アクセラレータ」\n",
|
||||
"\n",
|
||||
"でGPUを選択してください。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "oUKi1NYMmXrr"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (1) GPUの確認\n",
|
||||
"!nvidia-smi"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vV1t7PBRm-o6",
|
||||
"outputId": "785be88d-b741-4824-8b99-6863d172fa3b"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Sun Jan 29 12:17:09 2023 \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 510.47.03 Driver Version: 510.47.03 CUDA Version: 11.6 |\n",
|
||||
"|-------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|===============================+======================+======================|\n",
|
||||
"| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 67C P0 30W / 70W | 0MiB / 15360MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+-----------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=============================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+-----------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# リポジトリのクローン\n",
|
||||
"リポジトリをクローンします"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "sLBfykjBnjWc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (2) リポジトリのクローン\n",
|
||||
"!git clone --depth 1 https://github.com/w-okada/voice-changer.git -b v.1.3.7\n",
|
||||
"%cd voice-changer/server\n",
|
||||
"!git clone https://github.com/isletennos/MMVC_Client.git\n",
|
||||
"!cd MMVC_Client && git checkout 04f3fec4fd82dea6657026ec4e1cd80fb29a415c && cd -\n"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "86wTFmqsNMnD",
|
||||
"outputId": "b0bd865c-acec-47e5-b309-ac7fb6906f97"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Cloning into 'voice-changer'...\n",
|
||||
"remote: Enumerating objects: 157, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (157/157), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (141/141), done.\u001b[K\n",
|
||||
"remote: Total 157 (delta 21), reused 70 (delta 5), pack-reused 0\u001b[K\n",
|
||||
"Receiving objects: 100% (157/157), 1.61 MiB | 1.90 MiB/s, done.\n",
|
||||
"Resolving deltas: 100% (21/21), done.\n",
|
||||
"/content/voice-changer/server\n",
|
||||
"Cloning into 'MMVC_Client'...\n",
|
||||
"remote: Enumerating objects: 611, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (337/337), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (120/120), done.\u001b[K\n",
|
||||
"remote: Total 611 (delta 238), reused 285 (delta 214), pack-reused 274\u001b[K\n",
|
||||
"Receiving objects: 100% (611/611), 752.38 KiB | 875.00 KiB/s, done.\n",
|
||||
"Resolving deltas: 100% (360/360), done.\n",
|
||||
"Note: switching to '04f3fec4fd82dea6657026ec4e1cd80fb29a415c'.\n",
|
||||
"\n",
|
||||
"You are in 'detached HEAD' state. You can look around, make experimental\n",
|
||||
"changes and commit them, and you can discard any commits you make in this\n",
|
||||
"state without impacting any branches by switching back to a branch.\n",
|
||||
"\n",
|
||||
"If you want to create a new branch to retain commits you create, you may\n",
|
||||
"do so (now or later) by using -c with the switch command. Example:\n",
|
||||
"\n",
|
||||
" git switch -c <new-branch-name>\n",
|
||||
"\n",
|
||||
"Or undo this operation with:\n",
|
||||
"\n",
|
||||
" git switch -\n",
|
||||
"\n",
|
||||
"Turn off this advice by setting config variable advice.detachedHead to false\n",
|
||||
"\n",
|
||||
"HEAD is now at 04f3fec Merge pull request #30 from Mokuichi147/setupcheck\n",
|
||||
"/content/voice-changer/server\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# モジュールのインストール\n",
|
||||
"\n",
|
||||
"必要なモジュールをインストールします。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "8Na2PbLZSWgZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (3) 設定ファイルの確認\n",
|
||||
"!apt-get install -y libsndfile1-dev &> /dev/null\n",
|
||||
"!pip install fastapi &> /dev/null\n",
|
||||
"!pip install pyOpenSSL &> /dev/null\n",
|
||||
"!pip install python-multipart &> /dev/null\n",
|
||||
"!pip install python-socketio &> /dev/null\n",
|
||||
"!pip install uvicorn &> /dev/null\n",
|
||||
"!pip install websockets &> /dev/null\n",
|
||||
"!pip install onnxruntime-gpu &> /dev/null\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "LwZAAuqxX7yY"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# サーバの起動\n",
|
||||
"\n",
|
||||
"サーバを起動します。(4-1)\n",
|
||||
"\n",
|
||||
"サーバの起動状況を確認します。(4-2)\n",
|
||||
"\n",
|
||||
"このセルは繰り返し実行することになるのでCtrl+Retでセルを実行してください。\n",
|
||||
"\n",
|
||||
"下記のようなテキストが表示されたら起動完了です。\n",
|
||||
"\n",
|
||||
"**`DEBUG:asyncio:Using selector: EpollSelector`**\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
" Voice Changerを起動しています。\n",
|
||||
" -- 設定 --\n",
|
||||
" CONFIG:None, MODEL:None ONNX_MODEL:None\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "-_2OcN9Borke"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (4-1) サーバの起動\n",
|
||||
"import random\n",
|
||||
"PORT = 10000 + random.randint(1, 9999)\n",
|
||||
"LOG_FILE = f\"LOG_FILE_{PORT}\"\n",
|
||||
"\n",
|
||||
"get_ipython().system_raw(f'python3 MMVCServerSIO.py -t MMVC -p {PORT} --https False --colab True >{LOG_FILE} 2>&1 &')\n",
|
||||
"#print(f\"PORT:{PORT}, LOG_FILE:{LOG_FILE}\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "G-nMdPxEW1rc"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (4-2) サーバの起動確認\n",
|
||||
"!sleep 30\n",
|
||||
"!tail -20 {LOG_FILE}"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "chu06KpAjEK6",
|
||||
"outputId": "a9cb8947-3902-43ba-c3b9-e5857c0cc7b6"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"VoiceChanger Initialized (GPU_NUM:1, mps_enabled:False)\n",
|
||||
"\u001b[32m Voice Changerを起動しています。\u001b[0m\n",
|
||||
"\u001b[34m -- 設定 -- \u001b[0m\n",
|
||||
"\u001b[34m CONFIG:None, MODEL:None ONNX_MODEL:None\u001b[0m\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# プロキシを起動\n",
|
||||
"ウェブサーバへのアクセスをするためのプロキシを起動します。\n",
|
||||
"\n",
|
||||
"表示されたURLをクリックして開くと別タブでアプリが開きます。\n",
|
||||
"\n",
|
||||
"Colabなので、ロードにある程度時間がかかります(30秒くらい)。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "WhxcFLQEpctq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (5) プロキシを起動\n",
|
||||
"from google.colab.output import eval_js\n",
|
||||
"proxy = eval_js( \"google.colab.kernel.proxyPort(\" + str(PORT) + \")\" )\n",
|
||||
"print(f\"{proxy}front/?colab=true\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nkRjZm95l87C",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 34
|
||||
},
|
||||
"outputId": "5c2ff7a0-f10a-46b9-eaec-96d1874e255f"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"https://xzok7khbzs-496ff2e9c6d22116-13279-colab.googleusercontent.com/front/?colab=true\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [],
|
||||
"metadata": {
|
||||
"id": "axkt5BjhoiPV"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
@ -1,672 +0,0 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"authorship_tag": "ABX9TyMgL73lmseS7jsiBW+SSenN",
|
||||
"include_colab_link": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"gpuClass": "standard"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view-in-github",
|
||||
"colab_type": "text"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/w-okada/voice-changer/blob/master/VoiceRecorder.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# このノートブックは管理されていません。\n",
|
||||
"# VCClientをColabで使用する場合は、次のノートブックをご使用ください。\n",
|
||||
"\n",
|
||||
"# This notebook is not being managed.\n",
|
||||
"# If you're using VCClient in Colab, please use the following notebook.\n",
|
||||
"\n",
|
||||
"https://github.com/w-okada/voice-changer/blob/master/Realtime_Voice_Changer_on_Colab.ipynb\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "1ixPDugV0EA2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Voice Recorder\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"このノートでは、MMVCのトレーニング用の音声を録画するアプリ\"Voice Recorder\"をColab上から起動します。\n",
|
||||
"\n",
|
||||
"録音された音声はこのノートを通してGoogle Drive上にアップロードすることができます。\n",
|
||||
"\n",
|
||||
"また、従来のVoice Recorderと同様にローカルPCにダウンロードすることもできます。\n",
|
||||
"\n",
|
||||
"録音後にブラウザとcolab上のサーバ間でやり取りを行うので、更新に少しタイムラグが発生します。\n",
|
||||
"\n",
|
||||
"ご自身のPCでトレーニングを行う予定の場合は、colab上のサーバで録音するメリットはほぼありませんので、より快適な録音をするために[こちらのgithub上のVoice Recorder](https://w-okada.github.io/voice-changer/)をご使用ください。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"より詳細な情報はこちらの[リポジトリ](https://github.com/w-okada/voice-changer)からご確認いただけます。\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Lbbmx_Vjl0zo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# 録音データを格納するフォルダを指定\n",
|
||||
"\n",
|
||||
"フォルダは次の二つを指定する必要があります。\n",
|
||||
"1. 録音アプリ用のキャッシュデータ格納フォルダ\n",
|
||||
"2. トレーニングデータの格納フォルダ\n",
|
||||
"\n",
|
||||
"通常、録音データはGoogle Drive上のフォルダに格納すると思います。\n",
|
||||
"\n",
|
||||
"まずは(1-1)を実行してドライブをマウントしてください。\n",
|
||||
"\n",
|
||||
"その後、(1-2)で上記の格納フォルダを指定してください。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "mHvGrgaWnIPA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (1-1) Google Driveのマウント\n",
|
||||
"from google.colab import drive\n",
|
||||
"drive.mount('/content/drive')"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "Eihm8H2X-7wm",
|
||||
"outputId": "76331fb1-5ef8-40e6-a381-258b5e425853"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Mounted at /content/drive\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (1-2) 使用するモデルとコンフィグファイルの指定\n",
|
||||
"RECORDER_DATA_DIR=\"/content/drive/MyDrive/VoiceChanger/voice_data\"\n",
|
||||
"MMVC_DATA_DIR=\"/content/drive/MyDrive/VoiceChanger/dataset\"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nSXATMWYb4Ik"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# リポジトリのクローン\n",
|
||||
"リポジトリをクローンします"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "sLBfykjBnjWc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (2) リポジトリのクローン\n",
|
||||
"!git clone --depth 1 https://github.com/w-okada/voice-changer.git -b ver_1.0\n",
|
||||
"%cd voice-changer/docs/\n"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "86wTFmqsNMnD",
|
||||
"outputId": "40471833-d720-41c9-f4a7-ac15fbf18e14"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Cloning into 'voice-changer'...\n",
|
||||
"remote: Enumerating objects: 81, done.\u001b[K\n",
|
||||
"remote: Counting objects: 100% (81/81), done.\u001b[K\n",
|
||||
"remote: Compressing objects: 100% (68/68), done.\u001b[K\n",
|
||||
"remote: Total 81 (delta 12), reused 53 (delta 5), pack-reused 0\u001b[K\n",
|
||||
"Unpacking objects: 100% (81/81), done.\n",
|
||||
"Note: checking out 'f8823cb7e2025f13227f5918408cceda224bf9f0'.\n",
|
||||
"\n",
|
||||
"You are in 'detached HEAD' state. You can look around, make experimental\n",
|
||||
"changes and commit them, and you can discard any commits you make in this\n",
|
||||
"state without impacting any branches by performing another checkout.\n",
|
||||
"\n",
|
||||
"If you want to create a new branch to retain commits you create, you may\n",
|
||||
"do so (now or later) by using -b with the checkout command again. Example:\n",
|
||||
"\n",
|
||||
" git checkout -b <new-branch-name>\n",
|
||||
"\n",
|
||||
"/content/voice-changer/docs\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# ファイルの配置\n",
|
||||
"アプリケーションの挙動を記した設定ファイルをコピーします(3-1)。(3-2)はコピーした設定ファイルを表示しています。もしかしたらうまく動かないときに役立つかもしれません。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "jmDY8W_fnuSi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (3-1) 設定ファイルのコピー\n",
|
||||
"!cp ../template/setting_recorder_colab.json assets/setting.json"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "ow88ZaubluOJ"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (3-2) 設定ファイルの内容確認\n",
|
||||
"\n",
|
||||
"!cat assets/setting.json"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "rpWUobjlBCNF",
|
||||
"outputId": "285e0259-16af-4932-e78b-bec94f337e9c"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"{\n",
|
||||
" \"app_title\": \"voice-recorder\",\n",
|
||||
" \"storage_type\":\"server\",\n",
|
||||
" \"use_mel_spectrogram\":true,\n",
|
||||
" \"text\": [\n",
|
||||
" {\n",
|
||||
" \"title\": \"ITA-emotion\",\n",
|
||||
" \"wavPrefix\": \"emotion\",\n",
|
||||
" \"file\": \"./assets/text/ITA_emotion_all.txt\",\n",
|
||||
" \"file_hira\": \"./assets/text/ITA_emotion_all_hira.txt\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"title\": \"ITA-recitation\",\n",
|
||||
" \"wavPrefix\": \"recitation\",\n",
|
||||
" \"file\": \"./assets/text/ITA_recitation_all.txt\",\n",
|
||||
" \"file_hira\": \"./assets/text/ITA_recitation_all_hira.txt\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"title\": \"wagahaiwa\",\n",
|
||||
" \"wavPrefix\": \"wagahaiwa\",\n",
|
||||
" \"file\": \"./assets/text/wagahaiwa.txt\",\n",
|
||||
" \"file_hira\": \"./assets/text/wagahaiwa_hira.txt\"\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# モジュールのインストール\n",
|
||||
"\n",
|
||||
"必要なモジュールをインストールします。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "8Na2PbLZSWgZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (4) 設定ファイルの確認\n",
|
||||
"!pip install flask\n",
|
||||
"!pip install flask_cors\n"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "LwZAAuqxX7yY",
|
||||
"outputId": "ea2b3b39-d571-4d47-a38b-d0e657a335cd"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
|
||||
"Requirement already satisfied: flask in /usr/local/lib/python3.7/dist-packages (1.1.4)\n",
|
||||
"Requirement already satisfied: Jinja2<3.0,>=2.10.1 in /usr/local/lib/python3.7/dist-packages (from flask) (2.11.3)\n",
|
||||
"Requirement already satisfied: Werkzeug<2.0,>=0.15 in /usr/local/lib/python3.7/dist-packages (from flask) (1.0.1)\n",
|
||||
"Requirement already satisfied: click<8.0,>=5.1 in /usr/local/lib/python3.7/dist-packages (from flask) (7.1.2)\n",
|
||||
"Requirement already satisfied: itsdangerous<2.0,>=0.24 in /usr/local/lib/python3.7/dist-packages (from flask) (1.1.0)\n",
|
||||
"Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.7/dist-packages (from Jinja2<3.0,>=2.10.1->flask) (2.0.1)\n",
|
||||
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
|
||||
"Collecting flask_cors\n",
|
||||
" Downloading Flask_Cors-3.0.10-py2.py3-none-any.whl (14 kB)\n",
|
||||
"Requirement already satisfied: Six in /usr/local/lib/python3.7/dist-packages (from flask_cors) (1.15.0)\n",
|
||||
"Requirement already satisfied: Flask>=0.9 in /usr/local/lib/python3.7/dist-packages (from flask_cors) (1.1.4)\n",
|
||||
"Requirement already satisfied: Werkzeug<2.0,>=0.15 in /usr/local/lib/python3.7/dist-packages (from Flask>=0.9->flask_cors) (1.0.1)\n",
|
||||
"Requirement already satisfied: itsdangerous<2.0,>=0.24 in /usr/local/lib/python3.7/dist-packages (from Flask>=0.9->flask_cors) (1.1.0)\n",
|
||||
"Requirement already satisfied: Jinja2<3.0,>=2.10.1 in /usr/local/lib/python3.7/dist-packages (from Flask>=0.9->flask_cors) (2.11.3)\n",
|
||||
"Requirement already satisfied: click<8.0,>=5.1 in /usr/local/lib/python3.7/dist-packages (from Flask>=0.9->flask_cors) (7.1.2)\n",
|
||||
"Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.7/dist-packages (from Jinja2<3.0,>=2.10.1->Flask>=0.9->flask_cors) (2.0.1)\n",
|
||||
"Installing collected packages: flask-cors\n",
|
||||
"Successfully installed flask-cors-3.0.10\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# サーバの起動\n",
|
||||
"\n",
|
||||
"サーバを起動します。(5-1)\n",
|
||||
"\n",
|
||||
"サーバの起動状況を確認します。(5-2)\n",
|
||||
"\n",
|
||||
"このセルは繰り返し実行することになるのでCtrl+Retでセルを実行してください。\n",
|
||||
"\n",
|
||||
"アクセスできるようになるまで、数秒かかります。\n",
|
||||
"\n",
|
||||
"下記のようなテキストが表示されたら起動完了です。\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"[2022-09-13 22:20:49,936] INFO in recorderServer: START APP\n",
|
||||
" * Serving Flask app \"recorderServer\" (lazy loading)\n",
|
||||
" * Environment: production\n",
|
||||
" WARNING: This is a development server. Do not use it in a production deployment.\n",
|
||||
" Use a production WSGI server instead.\n",
|
||||
" * Debug mode: on\n",
|
||||
"[2022-09-13 22:20:49,946] INFO in _internal: * Running on http://0.0.0.0:8018/ (Press CTRL+C to quit)\n",
|
||||
"[2022-09-13 22:20:49,947] INFO in _internal: * Restarting with stat\n",
|
||||
"[2022-09-13 22:20:50,166] INFO in recorderServer: START APP\n",
|
||||
"[2022-09-13 22:20:50,174] WARNING in _internal: * Debugger is active!\n",
|
||||
"[2022-09-13 22:20:50,200] INFO in _internal: * Debugger PIN: 334-166-753\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "-_2OcN9Borke"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (5-1) サーバの起動\n",
|
||||
"PORT=8018\n",
|
||||
"get_ipython().system_raw(f'python3 recorderServer.py {PORT} {RECORDER_DATA_DIR} >foo 2>&1 &')"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "iNOAB7zISI6J"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (5-2) サーバの起動確認 (Ctrl+Retで実行)\n",
|
||||
"!cat foo"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "chu06KpAjEK6",
|
||||
"outputId": "6f5cbed9-65a7-4570-f58a-5447e402947c"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"[2022-11-08 19:11:17,679] INFO in recorderServer: START APP\n",
|
||||
" * Serving Flask app \"recorderServer\" (lazy loading)\n",
|
||||
" * Environment: production\n",
|
||||
" WARNING: This is a development server. Do not use it in a production deployment.\n",
|
||||
" Use a production WSGI server instead.\n",
|
||||
" * Debug mode: on\n",
|
||||
"[2022-11-08 19:11:17,696] INFO in _internal: * Running on http://0.0.0.0:8018/ (Press CTRL+C to quit)\n",
|
||||
"[2022-11-08 19:11:17,697] INFO in _internal: * Restarting with stat\n",
|
||||
"[2022-11-08 19:11:17,893] INFO in recorderServer: START APP\n",
|
||||
"[2022-11-08 19:11:17,900] WARNING in _internal: * Debugger is active!\n",
|
||||
"[2022-11-08 19:11:17,930] INFO in _internal: * Debugger PIN: 225-344-519\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# プロキシを起動\n",
|
||||
"ウェブサーバへのアクセスをするためのプロキシを起動します。\n",
|
||||
"\n",
|
||||
"表示されたURLをクリックして開くと別タブでアプリが開きます。\n",
|
||||
"\n",
|
||||
"Colabなので、ロードにある程度時間がかかります(30秒くらい)。"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "WhxcFLQEpctq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# (7) プロキシを起動\n",
|
||||
"from google.colab import output\n",
|
||||
"\n",
|
||||
"output.serve_kernel_port_as_window(PORT)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nkRjZm95l87C",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 34
|
||||
},
|
||||
"outputId": "7d2664b8-945c-4ee6-b49f-51f7f96cf388"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Javascript object>"
|
||||
],
|
||||
"application/javascript": [
|
||||
"(async (port, path, text, element) => {\n",
|
||||
" if (!google.colab.kernel.accessAllowed) {\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
" element.appendChild(document.createTextNode(''));\n",
|
||||
" const url = await google.colab.kernel.proxyPort(port);\n",
|
||||
" const anchor = document.createElement('a');\n",
|
||||
" anchor.href = new URL(path, url).toString();\n",
|
||||
" anchor.target = '_blank';\n",
|
||||
" anchor.setAttribute('data-href', url + path);\n",
|
||||
" anchor.textContent = text;\n",
|
||||
" element.appendChild(anchor);\n",
|
||||
" })(8018, \"/\", \"https://localhost:8018/\", window.element)"
|
||||
]
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# トレーニング用データフォルダ\n",
|
||||
"\n",
|
||||
"以下、トレーニング用のフォルダを作成します。\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "ZGuYYN7oCSM4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"corpus_id = \"14oXoQqLxRkP8NJK8qMYGee1_q2uEED1z\"\n",
|
||||
"\n",
|
||||
"data_setting = [\n",
|
||||
" [\"user\", \"\", \"\", \"00_myvoice\", \"107\"],\n",
|
||||
" [\"zundamon\", \"1h8Ajyvoig7Hl3LSSt2vYX0sUHX3JDF3R\", \"1205_zundamon\", \"01_target_zundamon\", \"100\"],\n",
|
||||
" [\"tsumugi\", \"14zE0F_5ZCQWXf6m6SUPF5Y3gpL6yb7zk\", \"344_tsumugi\", \"02_target_tsumugi\", \"103\"],\n",
|
||||
" [\"metan\", \"1iCrpzhqXm-0YdktOPM8M1pMtgQIDF3r4\", \"459_methane\", \"03_target_metan\", \"102\"],\n",
|
||||
" [\"sora\", \"1MXfMRG_sjbsaLihm7wEASG2PwuCponZF\", \"912_sora\", \"04_target_ksora\", \"101\"],\n",
|
||||
"]"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "3PhrmCD2LaCH"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import os, glob\n",
|
||||
"\n",
|
||||
"os.makedirs(MMVC_DATA_DIR, exist_ok=True)\n",
|
||||
"speaker_list = os.path.join(MMVC_DATA_DIR, \"multi_speaker_correspondence.txt\")\n",
|
||||
"!echo \"00_myvoice|107\" > {speaker_list}\n",
|
||||
"!echo \"01_target_zundamon|100\" >> {speaker_list}\n",
|
||||
"!echo \"02_target_tsumugi|103\" >> {speaker_list}\n",
|
||||
"!echo \"03_target_metan|102\" >> {speaker_list}\n",
|
||||
"!echo \"04_target_ksora|101\" >> {speaker_list}\n",
|
||||
"\n",
|
||||
"!cat {speaker_list}\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "f5l6ggSyACLs",
|
||||
"outputId": "4db3571a-46e6-4fd9-c560-628cf4af9284"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"00_myvoice|107\n",
|
||||
"01_target_zundamon|100\n",
|
||||
"02_target_tsumugi|103\n",
|
||||
"03_target_metan|102\n",
|
||||
"04_target_ksora|101\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"!rm -rf /content/drive/MyDrive/VoiceChanger/train_data/00_myvoice/wav/*"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "UEVb2GGZSesY"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import gdown\n",
|
||||
"\n",
|
||||
"gdown.download(f'https://drive.google.com/uc?id={corpus_id}', f'ita_corpus.zip', quiet=False)\n",
|
||||
"!unzip -oq 'ita_corpus.zip'\n",
|
||||
"\n",
|
||||
"for chara in data_setting:\n",
|
||||
" chara_root_dir = os.path.join(MMVC_DATA_DIR, chara[3])\n",
|
||||
" os.makedirs(chara_root_dir, exist_ok=True)\n",
|
||||
"\n",
|
||||
" chara_text_dir = os.path.join(chara_root_dir, \"text\")\n",
|
||||
" os.makedirs(chara_text_dir, exist_ok=True)\n",
|
||||
" chara_wav_dir = os.path.join(chara_root_dir, \"wav\")\n",
|
||||
" os.makedirs(chara_wav_dir, exist_ok=True)\n",
|
||||
"\n",
|
||||
" if chara[0] != \"user\":\n",
|
||||
" gdown.download(f'https://drive.google.com/uc?id={chara[1]}', f'{chara[0]}.zip', quiet=False)\n",
|
||||
" !unzip -f '{chara[0]}.zip'\n",
|
||||
" !cp -rf {chara[2]}/* {chara_root_dir}/\n",
|
||||
"\n",
|
||||
" if chara[0] == \"user\":\n",
|
||||
" !cp MMVC向けITAコーパス文章ファイル_配布用/ITA_emotion_hira_100file/* {chara_text_dir}\n",
|
||||
" !cp MMVC向けITAコーパス文章ファイル_配布用/ITA_recitation_hira_324file/* {chara_text_dir}\n",
|
||||
"\n",
|
||||
" file_list = [os.path.abspath(p) for p in glob.glob(f\"{RECORDER_DATA_DIR}/*/*.zip\")]\n",
|
||||
" for f in list(file_list):\n",
|
||||
" # print(f)\n",
|
||||
" basename = os.path.basename(f)\n",
|
||||
" wavname = os.path.splitext(basename)[0] + \".wav\"\n",
|
||||
" full_path = os.path.join(chara_wav_dir, wavname)\n",
|
||||
" # print(basename, wavname, full_path)\n",
|
||||
" !unzip -oq {f} vf24kTrim.wav\n",
|
||||
" !cp vf24kTrim.wav {full_path}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "L8UsVp3dDs4R",
|
||||
"outputId": "5d640caf-87b0-45a6-aa0c-76295e537f6a"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"Downloading...\n",
|
||||
"From: https://drive.google.com/uc?id=14oXoQqLxRkP8NJK8qMYGee1_q2uEED1z\n",
|
||||
"To: /content/voice-changer/docs/ita_corpus.zip\n",
|
||||
"100%|██████████| 1.20M/1.20M [00:00<00:00, 87.9MB/s]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"/content/drive/MyDrive/VoiceChanger/voice_data/ITA-emotion/emotion000.zip\n",
|
||||
"/content/drive/MyDrive/VoiceChanger/voice_data/ITA-emotion/emotion002.zip\n",
|
||||
"/content/drive/MyDrive/VoiceChanger/voice_data/ITA-emotion/emotion001.zip\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"Downloading...\n",
|
||||
"From: https://drive.google.com/uc?id=1h8Ajyvoig7Hl3LSSt2vYX0sUHX3JDF3R\n",
|
||||
"To: /content/voice-changer/docs/zundamon.zip\n",
|
||||
"100%|██████████| 55.6M/55.6M [00:00<00:00, 251MB/s]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Archive: zundamon.zip\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"Downloading...\n",
|
||||
"From: https://drive.google.com/uc?id=14zE0F_5ZCQWXf6m6SUPF5Y3gpL6yb7zk\n",
|
||||
"To: /content/voice-changer/docs/tsumugi.zip\n",
|
||||
"100%|██████████| 73.0M/73.0M [00:00<00:00, 226MB/s]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Archive: tsumugi.zip\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"Downloading...\n",
|
||||
"From: https://drive.google.com/uc?id=1iCrpzhqXm-0YdktOPM8M1pMtgQIDF3r4\n",
|
||||
"To: /content/voice-changer/docs/metan.zip\n",
|
||||
"100%|██████████| 51.8M/51.8M [00:00<00:00, 219MB/s]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Archive: metan.zip\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"Downloading...\n",
|
||||
"From: https://drive.google.com/uc?id=1MXfMRG_sjbsaLihm7wEASG2PwuCponZF\n",
|
||||
"To: /content/voice-changer/docs/sora.zip\n",
|
||||
"100%|██████████| 70.2M/70.2M [00:00<00:00, 184MB/s]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Archive: sora.zip\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [],
|
||||
"metadata": {
|
||||
"id": "yHmaXx31EOta"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
11
client/.vscode/settings.json
vendored
Normal file
11
client/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"tab.activeBackground": "#65952acc"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.printWidth": 1024,
|
||||
"prettier.tabWidth": 4,
|
||||
"files.associations": {
|
||||
"*.css": "postcss"
|
||||
}
|
||||
}
|
9
client/demo/.vscode/settings.json
vendored
9
client/demo/.vscode/settings.json
vendored
@ -1,8 +1,11 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"*.css": "postcss"
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"tab.activeBackground": "#65952acc"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.printWidth": 1024,
|
||||
"prettier.tabWidth": 4,
|
||||
"files.associations": {
|
||||
"*.css": "postcss"
|
||||
}
|
||||
}
|
||||
|
11
client/demo/build-voice-changer-js.sh
Normal file
11
client/demo/build-voice-changer-js.sh
Normal file
@ -0,0 +1,11 @@
|
||||
# cd ~/git-work/voice-changer-js/lib/ ; npm run build:dev; cd -
|
||||
# rm -rf node_modules/@dannadori/voice-changer-js
|
||||
# mkdir -p node_modules/@dannadori/voice-changer-js/dist
|
||||
# cp -r ~/git-work/voice-changer-js/lib/package.json node_modules/@dannadori/voice-changer-js/
|
||||
# cp -r ~/git-work/voice-changer-js/lib/dist node_modules/@dannadori/voice-changer-js/
|
||||
|
||||
cd ~/git-work/voice-changer-js/lib/ ; npm run build:prod; cd -
|
||||
rm -rf node_modules/@dannadori/voice-changer-js
|
||||
mkdir -p node_modules/@dannadori/voice-changer-js/dist
|
||||
cp -r ~/git-work/voice-changer-js/lib/package.json node_modules/@dannadori/voice-changer-js/
|
||||
cp -r ~/git-work/voice-changer-js/lib/dist node_modules/@dannadori/voice-changer-js/
|
928
client/demo/dist/assets/beatrice/female-clickable.svg
vendored
Normal file
928
client/demo/dist/assets/beatrice/female-clickable.svg
vendored
Normal file
@ -0,0 +1,928 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:ns2="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="100 60 420 450" version="1.1">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<ns2:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2023-11-19T11:21:56.358384</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<ns2:Agent>
|
||||
<dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title>
|
||||
</ns2:Agent>
|
||||
</dc:creator>
|
||||
</ns2:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
* {
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: butt
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.beatrice-node-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer:hover {
|
||||
stroke: gray;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer-selected {
|
||||
stroke: #ef6767c2;
|
||||
stroke-width: 3
|
||||
}
|
||||
|
||||
.beatrice-text-pointer {
|
||||
cursor: pointer;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.beatrice-text-pointer:hover {
|
||||
/* ホバー時のスタイルは既に設定されたスタイルと異なる特定の属性を変更することができます。 */
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 576 L 576 576 L 576 0 L 0 0 z " style="fill: #ffffff" />
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="LineCollection_1">
|
||||
<path d="M 403.96157 149.258085 L 366.630583 148.159991 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 372.120414 365.421971 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 416.760989 346.999139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 258.035169 326.134244 L 298.859694 332.465911 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 167.453327 366.897955 L 203.987537 347.931194 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 436.352807 416.173738 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 417.560846 259.464346 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 424.070309 219.021704 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 205.541044 459.711101 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 160.44225 292.540336 L 167.396334 325.961848 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.679012 107.607273 L 366.630583 148.159991 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 325.345004 219.195921 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 325.345004 219.195921 L 297.530501 194.55124 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 363.075301 201.701937 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 363.075301 201.701937 L 341.462109 170.414842 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 366.630583 148.159991 L 341.462109 170.414842 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 167.396334 325.961848 L 203.987537 347.931194 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 262.111309 181.887977 L 297.530501 194.55124 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 189.293496 262.735141 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 277.95603 462.622539 L 293.230217 421.393405 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 333.932593 269.342364 L 301.9174 258.124913 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 333.932593 269.342364 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 334.666605 338.097578 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 203.987537 347.931194 L 242.811958 354.082183 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 276.198518 388.99868 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 298.859694 332.465911 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 242.811958 354.082183 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 276.198518 388.99868 L 293.230217 421.393405 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 293.230217 421.393405 L 309.530924 454.827332 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 293.230217 421.393405 L 260.004712 426.426278 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 423.853712 378.321354 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 205.214352 217.066163 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 154.047193 423.153273 L 193.933786 408.004355 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 298.859694 332.465911 L 277.79477 306.980241 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 277.79477 306.980241 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 228.689744 409.959215 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 261.506403 474.152727 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 301.9174 258.124913 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 254.897329 263.033159 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 254.897329 263.033159 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 321.267463 403.021207 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 372.120414 365.421971 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 351.226176 419.342667 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 404.238335 402.754731 L 400.607869 434.730447 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 193.933786 408.004355 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
</g>
|
||||
<g id="PathCollection_1">
|
||||
<defs>
|
||||
<path id="C0_0_b0ffb3bf4a"
|
||||
d="M 0 11.18034 C 2.965061 11.18034 5.80908 10.002309 7.905694 7.905694 C 10.002309 5.80908 11.18034 2.965061 11.18034 -0 C 11.18034 -2.965061 10.002309 -5.80908 7.905694 -7.905694 C 5.80908 -10.002309 2.965061 -11.18034 0 -11.18034 C -2.965061 -11.18034 -5.80908 -10.002309 -7.905694 -7.905694 C -10.002309 -5.80908 -11.18034 -2.965061 -11.18034 0 C -11.18034 2.965061 -10.002309 5.80908 -7.905694 7.905694 C -5.80908 10.002309 -2.965061 11.18034 0 11.18034 z " />
|
||||
</defs>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-0"
|
||||
onclick="(()=>{console.log('node 0')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="403.96157" y="149.258085" style="fill: #e7f5d2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-1"
|
||||
onclick="(()=>{console.log('node 1')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="396.547407" y="371.476481" style="fill: #fbe8f2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-2"
|
||||
onclick="(()=>{console.log('node 2')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="258.035169" y="326.134244" style="fill: #cfebaa" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-3"
|
||||
onclick="(()=>{console.log('node 3')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="167.453327" y="366.897955" style="fill: #f1f6e8" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-4"
|
||||
onclick="(()=>{console.log('node 4')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="436.352807" y="416.173738" style="fill: #e89ac6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-5"
|
||||
onclick="(()=>{console.log('node 5')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="391.514336" y="242.048236" style="fill: #f3bcdd" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-6"
|
||||
onclick="(()=>{console.log('node 6')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="205.541044" y="459.711101" style="fill: #fbd9ec" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-7"
|
||||
onclick="(()=>{console.log('node 7')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="160.44225" y="292.540336" style="fill: #9ed067" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-8"
|
||||
onclick="(()=>{console.log('node 8')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="424.070309" y="219.021704" style="fill: #e1f3c7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-9"
|
||||
onclick="(()=>{console.log('node 9')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="345.679012" y="107.607273" style="fill: #d0ecad" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-10"
|
||||
onclick="(()=>{console.log('node 10')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="325.345004" y="219.195921" style="fill: #eff6e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-11"
|
||||
onclick="(()=>{console.log('node 11')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="363.075301" y="201.701937" style="fill: #f9f0f5" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-12"
|
||||
onclick="(()=>{console.log('node 12')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="366.630583" y="148.159991" style="fill: #ebf6dc" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-13"
|
||||
onclick="(()=>{console.log('node 13')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="341.462109" y="170.414842" style="fill: #fad6ea" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-14"
|
||||
onclick="(()=>{console.log('node 14')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="167.396334" y="325.961848" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-15"
|
||||
onclick="(()=>{console.log('node 15')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="262.111309" y="181.887977" style="fill: #e9f5d6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-16"
|
||||
onclick="(()=>{console.log('node 16')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="189.293496" y="262.735141" style="fill: #fce5f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-17"
|
||||
onclick="(()=>{console.log('node 17')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="277.95603" y="462.622539" style="fill: #c4e699" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-18"
|
||||
onclick="(()=>{console.log('node 18')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="333.932593" y="269.342364" style="fill: #f8f4f6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-19"
|
||||
onclick="(()=>{console.log('node 19')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="416.760989" y="346.999139" style="fill: #eef6e2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-20"
|
||||
onclick="(()=>{console.log('node 20')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="334.666605" y="338.097578" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-21"
|
||||
onclick="(()=>{console.log('node 21')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="203.987537" y="347.931194" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-22"
|
||||
onclick="(()=>{console.log('node 22')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="288.059985" y="363.924972" style="fill: #ddf1c1" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-23"
|
||||
onclick="(()=>{console.log('node 23')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="276.198518" y="388.99868" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-24"
|
||||
onclick="(()=>{console.log('node 24')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="293.230217" y="421.393405" style="fill: #f3f7ef" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-25"
|
||||
onclick="(()=>{console.log('node 25')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="423.853712" y="378.321354" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-26"
|
||||
onclick="(()=>{console.log('node 26')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="205.214352" y="217.066163" style="fill: #e7f5d2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-27"
|
||||
onclick="(()=>{console.log('node 27')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="242.811958" y="354.082183" style="fill: #d2ecb0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-28"
|
||||
onclick="(()=>{console.log('node 28')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="154.047193" y="423.153273" style="fill: #e6f5d0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-29"
|
||||
onclick="(()=>{console.log('node 29')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="298.859694" y="332.465911" style="fill: #ecf6de" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-30"
|
||||
onclick="(()=>{console.log('node 30')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="277.79477" y="306.980241" style="fill: #eaf5d9" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-31"
|
||||
onclick="(()=>{console.log('node 31')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="260.004712" y="426.426278" style="fill: #f9f1f5" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-32"
|
||||
onclick="(()=>{console.log('node 32')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="301.9174" y="258.124913" style="fill: #dbf0bf" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-33"
|
||||
onclick="(()=>{console.log('node 33')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="254.897329" y="263.033159" style="fill: #eff6e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-34"
|
||||
onclick="(()=>{console.log('node 34')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="321.267463" y="403.021207" style="fill: #d0ecad" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-35"
|
||||
onclick="(()=>{console.log('node 35')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="345.750267" y="380.131711" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-36"
|
||||
onclick="(()=>{console.log('node 36')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="404.238335" y="402.754731" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-37"
|
||||
onclick="(()=>{console.log('node 37')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="355.734145" y="235.68791" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-38"
|
||||
onclick="(()=>{console.log('node 38')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="193.933786" y="408.004355" style="fill: #f0f6e7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-39"
|
||||
onclick="(()=>{console.log('node 39')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="297.530501" y="194.55124" style="fill: #f3f6ed" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-40"
|
||||
onclick="(()=>{console.log('node 40')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="320.07566" y="368.578481" style="fill: #dbf0bf" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-41"
|
||||
onclick="(()=>{console.log('node 41')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="228.689744" y="409.959215" style="fill: #f9eff4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-42"
|
||||
onclick="(()=>{console.log('node 42')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="351.226176" y="419.342667" style="fill: #cfebaa" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-43"
|
||||
onclick="(()=>{console.log('node 43')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="372.120414" y="365.421971" style="fill: #f7f6f7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-44"
|
||||
onclick="(()=>{console.log('node 44')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="230.303076" y="436.148139" style="fill: #f8cee6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-45"
|
||||
onclick="(()=>{console.log('node 45')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="261.506403" y="474.152727" style="fill: #e6f5d0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-46"
|
||||
onclick="(()=>{console.log('node 46')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="417.560846" y="259.464346" style="fill: #b7e085" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-47"
|
||||
onclick="(()=>{console.log('node 47')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="400.607869" y="434.730447" style="fill: #f8cee6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-48"
|
||||
onclick="(()=>{console.log('node 48')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="282.261978" y="282.779534" style="fill: #d6eeb6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-49"
|
||||
onclick="(()=>{console.log('node 49')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="222.122563" y="261.416721" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-50"
|
||||
onclick="(()=>{console.log('node 50')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="309.530924" y="454.827332" style="fill: #f9eef4" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-0" onclick="(()=>{console.log('text 0 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(399.786883 152.569335) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-32"
|
||||
d="M 1844 884 L 3897 884 L 3897 0 L 506 0 L 506 884 L 2209 2388 Q 2438 2594 2547 2791 Q 2656 2988 2656 3200 Q 2656 3528 2436 3728 Q 2216 3928 1850 3928 Q 1569 3928 1234 3808 Q 900 3688 519 3450 L 519 4475 Q 925 4609 1322 4679 Q 1719 4750 2100 4750 Q 2938 4750 3402 4381 Q 3866 4013 3866 3353 Q 3866 2972 3669 2642 Q 3472 2313 2841 1759 L 1844 884 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-1" onclick="(()=>{console.log('text 1 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(392.37272 374.787731) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-34"
|
||||
d="M 2356 3675 L 1038 1722 L 2356 1722 L 2356 3675 z M 2156 4666 L 3494 4666 L 3494 1722 L 4159 1722 L 4159 850 L 3494 850 L 3494 0 L 2356 0 L 2356 850 L 288 850 L 288 1881 L 2156 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-2" onclick="(()=>{console.log('text 2 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.860482 329.445494) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-37"
|
||||
d="M 428 4666 L 3944 4666 L 3944 3988 L 2125 0 L 953 0 L 2675 3781 L 428 3781 L 428 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-3" onclick="(()=>{console.log('text 3 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(163.27864 370.209205) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-38"
|
||||
d="M 2228 2088 Q 1891 2088 1709 1903 Q 1528 1719 1528 1375 Q 1528 1031 1709 848 Q 1891 666 2228 666 Q 2563 666 2741 848 Q 2919 1031 2919 1375 Q 2919 1722 2741 1905 Q 2563 2088 2228 2088 z M 1350 2484 Q 925 2613 709 2878 Q 494 3144 494 3541 Q 494 4131 934 4440 Q 1375 4750 2228 4750 Q 3075 4750 3515 4442 Q 3956 4134 3956 3541 Q 3956 3144 3739 2878 Q 3522 2613 3097 2484 Q 3572 2353 3814 2058 Q 4056 1763 4056 1313 Q 4056 619 3595 264 Q 3134 -91 2228 -91 Q 1319 -91 855 264 Q 391 619 391 1313 Q 391 1763 633 2058 Q 875 2353 1350 2484 z M 1631 3419 Q 1631 3141 1786 2991 Q 1941 2841 2228 2841 Q 2509 2841 2662 2991 Q 2816 3141 2816 3419 Q 2816 3697 2662 3845 Q 2509 3994 2228 3994 Q 1941 3994 1786 3844 Q 1631 3694 1631 3419 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-4" onclick="(()=>{console.log('text 4 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(428.003432 419.484988) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-31"
|
||||
d="M 750 831 L 1813 831 L 1813 3847 L 722 3622 L 722 4441 L 1806 4666 L 2950 4666 L 2950 831 L 4013 831 L 4013 0 L 750 0 L 750 831 z "
|
||||
transform="scale(0.015625)" />
|
||||
<path id="DejaVuSans-Bold-30"
|
||||
d="M 2944 2338 Q 2944 3213 2780 3570 Q 2616 3928 2228 3928 Q 1841 3928 1675 3570 Q 1509 3213 1509 2338 Q 1509 1453 1675 1090 Q 1841 728 2228 728 Q 2613 728 2778 1090 Q 2944 1453 2944 2338 z M 4147 2328 Q 4147 1169 3647 539 Q 3147 -91 2228 -91 Q 1306 -91 806 539 Q 306 1169 306 2328 Q 306 3491 806 4120 Q 1306 4750 2228 4750 Q 3147 4750 3647 4120 Q 4147 3491 4147 2328 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-5" onclick="(()=>{console.log('text 5 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(383.164961 245.359486) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-6" onclick="(()=>{console.log('text 6 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(197.191669 463.022351) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-35"
|
||||
d="M 678 4666 L 3669 4666 L 3669 3781 L 1638 3781 L 1638 3059 Q 1775 3097 1914 3117 Q 2053 3138 2203 3138 Q 3056 3138 3531 2711 Q 4006 2284 4006 1522 Q 4006 766 3489 337 Q 2972 -91 2053 -91 Q 1656 -91 1267 -14 Q 878 63 494 219 L 494 1166 Q 875 947 1217 837 Q 1559 728 1863 728 Q 2300 728 2551 942 Q 2803 1156 2803 1522 Q 2803 1891 2551 2103 Q 2300 2316 1863 2316 Q 1603 2316 1309 2248 Q 1016 2181 678 2041 L 678 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-7" onclick="(()=>{console.log('text 7 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(152.092875 295.851586) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-36"
|
||||
d="M 2316 2303 Q 2000 2303 1842 2098 Q 1684 1894 1684 1484 Q 1684 1075 1842 870 Q 2000 666 2316 666 Q 2634 666 2792 870 Q 2950 1075 2950 1484 Q 2950 1894 2792 2098 Q 2634 2303 2316 2303 z M 3803 4544 L 3803 3681 Q 3506 3822 3243 3889 Q 2981 3956 2731 3956 Q 2194 3956 1894 3657 Q 1594 3359 1544 2772 Q 1750 2925 1990 3001 Q 2231 3078 2516 3078 Q 3231 3078 3670 2659 Q 4109 2241 4109 1563 Q 4109 813 3618 361 Q 3128 -91 2303 -91 Q 1394 -91 895 523 Q 397 1138 397 2266 Q 397 3422 980 4083 Q 1563 4744 2578 4744 Q 2900 4744 3203 4694 Q 3506 4644 3803 4544 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-8" onclick="(()=>{console.log('text 8 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(415.720934 222.332954) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-9" onclick="(()=>{console.log('text 9 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(337.329637 110.918523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-10" onclick="(()=>{console.log('text 10 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(316.995629 222.507171) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-39"
|
||||
d="M 641 103 L 641 966 Q 928 831 1190 764 Q 1453 697 1709 697 Q 2247 697 2547 995 Q 2847 1294 2900 1881 Q 2688 1725 2447 1647 Q 2206 1569 1925 1569 Q 1209 1569 770 1986 Q 331 2403 331 3084 Q 331 3838 820 4291 Q 1309 4744 2131 4744 Q 3044 4744 3544 4128 Q 4044 3513 4044 2388 Q 4044 1231 3459 570 Q 2875 -91 1856 -91 Q 1528 -91 1228 -42 Q 928 6 641 103 z M 2125 2350 Q 2441 2350 2600 2554 Q 2759 2759 2759 3169 Q 2759 3575 2600 3781 Q 2441 3988 2125 3988 Q 1809 3988 1650 3781 Q 1491 3575 1491 3169 Q 1491 2759 1650 2554 Q 1809 2350 2125 2350 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-11" onclick="(()=>{console.log('text 11 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(354.725926 205.013187) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-12" onclick="(()=>{console.log('text 12 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(358.281208 151.471241) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-13" onclick="(()=>{console.log('text 13 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(333.112734 173.726092) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-14" onclick="(()=>{console.log('text 14 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(159.046959 329.273098) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-15" onclick="(()=>{console.log('text 15 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.761934 185.199227) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-16" onclick="(()=>{console.log('text 16 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(180.944121 266.046391) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-33"
|
||||
d="M 2981 2516 Q 3453 2394 3698 2092 Q 3944 1791 3944 1325 Q 3944 631 3412 270 Q 2881 -91 1863 -91 Q 1503 -91 1142 -33 Q 781 25 428 141 L 428 1069 Q 766 900 1098 814 Q 1431 728 1753 728 Q 2231 728 2486 893 Q 2741 1059 2741 1369 Q 2741 1688 2480 1852 Q 2219 2016 1709 2016 L 1228 2016 L 1228 2791 L 1734 2791 Q 2188 2791 2409 2933 Q 2631 3075 2631 3366 Q 2631 3634 2415 3781 Q 2200 3928 1806 3928 Q 1516 3928 1219 3862 Q 922 3797 628 3669 L 628 4550 Q 984 4650 1334 4700 Q 1684 4750 2022 4750 Q 2931 4750 3382 4451 Q 3834 4153 3834 3553 Q 3834 3144 3618 2883 Q 3403 2622 2981 2516 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-17" onclick="(()=>{console.log('text 17 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(269.606655 465.933789) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-18" onclick="(()=>{console.log('text 18 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(325.583218 272.653614) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-19" onclick="(()=>{console.log('text 19 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(408.411614 350.310389) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-20" onclick="(()=>{console.log('text 20 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(326.31723 341.408828) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-21" onclick="(()=>{console.log('text 21 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(195.638162 351.242444) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-22" onclick="(()=>{console.log('text 22 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(279.71061 367.236222) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-23" onclick="(()=>{console.log('text 23 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(267.849143 392.30993) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-24" onclick="(()=>{console.log('text 24 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(284.880842 424.704655) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-25" onclick="(()=>{console.log('text 25 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(415.504337 381.632604) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-26" onclick="(()=>{console.log('text 26 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(196.864977 220.377413) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-27" onclick="(()=>{console.log('text 27 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(234.462583 357.393433) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-28" onclick="(()=>{console.log('text 28 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(145.697818 426.464523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-29" onclick="(()=>{console.log('text 29 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(290.510319 335.777161) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-30" onclick="(()=>{console.log('text 30 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(269.445395 310.291491) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-31" onclick="(()=>{console.log('text 31 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(251.655337 429.737528) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-32" onclick="(()=>{console.log('text 32 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(293.568025 261.436163) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-33" onclick="(()=>{console.log('text 33 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(246.547954 266.344409) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-34" onclick="(()=>{console.log('text 34 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(312.918088 406.332457) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-35" onclick="(()=>{console.log('text 35 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(337.400892 383.442961) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-36" onclick="(()=>{console.log('text 36 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(395.88896 406.065981) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-37" onclick="(()=>{console.log('text 37 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(347.38477 238.99916) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-38" onclick="(()=>{console.log('text 38 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(185.584411 411.315605) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-39" onclick="(()=>{console.log('text 39 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(289.181126 197.86249) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-40" onclick="(()=>{console.log('text 40 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(311.726285 371.889731) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-41" onclick="(()=>{console.log('text 41 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(220.340369 413.270465) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-42" onclick="(()=>{console.log('text 42 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(342.876801 422.653917) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-43" onclick="(()=>{console.log('text 43 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(363.771039 368.733221) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-44" onclick="(()=>{console.log('text 44 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(221.953701 439.459389) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-45" onclick="(()=>{console.log('text 45 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.157028 477.463977) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-46" onclick="(()=>{console.log('text 46 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(409.211471 262.775596) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-47" onclick="(()=>{console.log('text 47 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(392.258494 438.041697) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-48" onclick="(()=>{console.log('text 48 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(273.912603 286.090784) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-49" onclick="(()=>{console.log('text 49 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(213.773188 264.727971) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-50" onclick="(()=>{console.log('text 50 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(301.181549 458.138582) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pe3de578e26">
|
||||
<rect x="124.405104" y="69.12" width="341.589792" height="443.52" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 56 KiB |
898
client/demo/dist/assets/beatrice/male-clickable.svg
vendored
Normal file
898
client/demo/dist/assets/beatrice/male-clickable.svg
vendored
Normal file
@ -0,0 +1,898 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:ns2="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="100 60 420 450" version="1.1">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<ns2:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2023-11-19T11:21:55.705408</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<ns2:Agent>
|
||||
<dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title>
|
||||
</ns2:Agent>
|
||||
</dc:creator>
|
||||
</ns2:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
* {
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: butt
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.beatrice-node-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer:hover {
|
||||
stroke: gray;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer-selected {
|
||||
stroke: #ef6767c2;
|
||||
stroke-width: 3
|
||||
}
|
||||
|
||||
.beatrice-text-pointer {
|
||||
cursor: pointer;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.beatrice-text-pointer:hover {
|
||||
/* ホバー時のスタイルは既に設定されたスタイルと異なる特定の属性を変更することができます。 */
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 576 L 576 576 L 576 0 L 0 0 z " style="fill: #ffffff" />
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="LineCollection_1">
|
||||
<path d="M 383.475478 335.382791 L 350.123561 336.312105 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 383.475478 335.382791 L 393.562573 295.917472 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 383.475478 335.382791 L 396.396073 371.656412 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 395.592267 184.349842 L 344.302973 166.290216 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 166.614267 246.553188 L 214.405523 244.575019 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 389.299516 416.267064 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 367.134249 434.454954 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 396.396073 371.656412 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 321.091057 403.95329 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 291.699254 114.456198 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 309.72476 346.813492 L 326.464644 303.679747 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.396073 371.656412 L 422.276969 403.842356 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.396073 371.656412 L 419.504487 334.14189 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 278.840744 190.572938 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 344.302973 166.290216 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 216.196468 317.113868 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 169.41455 268.66905 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 341.073251 272.287852 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 350.123561 336.312105 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 468.104517 290.196764 L 453.314054 329.209099 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 252.522958 107.607273 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 241.817 158.353487 L 278.840744 190.572938 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 278.840744 190.572938 L 264.569363 223.030096 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 190.32114 223.314542 L 214.405523 244.575019 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 233.41271 348.401671 L 216.196468 317.113868 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 122.295483 352.502553 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 158.624602 400.46174 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 214.405523 244.575019 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 214.405523 244.575019 L 207.563253 203.013335 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 298.808991 236.296491 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 236.649711 206.251683 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 220.79649 394.869471 L 213.931911 434.927829 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 220.79649 394.869471 L 208.453065 361.465389 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 241.321249 255.242558 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 257.120877 296.156882 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 453.314054 329.209099 L 419.504487 334.14189 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 287.180764 309.56402 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 287.429936 148.935339 L 321.071206 134.027026 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 300.632415 433.812968 L 321.091057 403.95329 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 216.196468 317.113868 L 181.944484 318.835753 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 216.196468 317.113868 L 208.453065 361.465389 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 419.504487 334.14189 L 436.061109 363.566053 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 366.514998 474.152727 L 367.134249 434.454954 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 208.453065 361.465389 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
</g>
|
||||
<g id="PathCollection_1">
|
||||
<defs>
|
||||
<path id="C0_0_3858269516"
|
||||
d="M 0 11.18034 C 2.965061 11.18034 5.80908 10.002309 7.905694 7.905694 C 10.002309 5.80908 11.18034 2.965061 11.18034 -0 C 11.18034 -2.965061 10.002309 -5.80908 7.905694 -7.905694 C 5.80908 -10.002309 2.965061 -11.18034 0 -11.18034 C -2.965061 -11.18034 -5.80908 -10.002309 -7.905694 -7.905694 C -10.002309 -5.80908 -11.18034 -2.965061 -11.18034 0 C -11.18034 2.965061 -10.002309 5.80908 -7.905694 7.905694 C -5.80908 10.002309 -2.965061 11.18034 0 11.18034 z " />
|
||||
</defs>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-0" onclick="(()=>{console.log('node 0')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="383.475478" y="335.382791" style="fill: #fde2bb" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-1" onclick="(()=>{console.log('node 1')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="393.562573" y="295.917472" style="fill: #fdba68" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-2" onclick="(()=>{console.log('node 2')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="395.592267" y="184.349842" style="fill: #fbe9cf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-3" onclick="(()=>{console.log('node 3')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="166.614267" y="246.553188" style="fill: #7e70ab" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-4" onclick="(()=>{console.log('node 4')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="362.886037" y="395.352171" style="fill: #e8e9f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-5" onclick="(()=>{console.log('node 5')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="291.699254" y="114.456198" style="fill: #f9b158" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-6" onclick="(()=>{console.log('node 6')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="309.72476" y="346.813492" style="fill: #e4e5f0" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-7" onclick="(()=>{console.log('node 7')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="396.396073" y="371.656412" style="fill: #fdcc8c" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-8" onclick="(()=>{console.log('node 8')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="311.713" y="188.802087" style="fill: #fedeb3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-9" onclick="(()=>{console.log('node 9')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="213.805036" y="285.720019" style="fill: #bab5d7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-10"
|
||||
onclick="(()=>{console.log('node 10')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="326.464644" y="303.679747" style="fill: #eaebf2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-11"
|
||||
onclick="(()=>{console.log('node 11')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="468.104517" y="290.196764" style="fill: #f7f7f6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-12"
|
||||
onclick="(()=>{console.log('node 12')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="169.41455" y="268.66905" style="fill: #dfe1ee" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-13"
|
||||
onclick="(()=>{console.log('node 13')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="252.522958" y="107.607273" style="fill: #eff0f4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-14"
|
||||
onclick="(()=>{console.log('node 14')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="241.817" y="158.353487" style="fill: #e58a20" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-15"
|
||||
onclick="(()=>{console.log('node 15')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="278.840744" y="190.572938" style="fill: #fedbac" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-16"
|
||||
onclick="(()=>{console.log('node 16')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="190.32114" y="223.314542" style="fill: #dfe1ee" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-17"
|
||||
onclick="(()=>{console.log('node 17')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="233.41271" y="348.401671" style="fill: #c3c0dd" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-18"
|
||||
onclick="(()=>{console.log('node 18')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="122.295483" y="352.502553" style="fill: #fed8a6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-19"
|
||||
onclick="(()=>{console.log('node 19')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="158.624602" y="400.46174" style="fill: #f7f6f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-20"
|
||||
onclick="(()=>{console.log('node 20')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="214.405523" y="244.575019" style="fill: #f9f2e9" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-21"
|
||||
onclick="(()=>{console.log('node 21')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="264.569363" y="223.030096" style="fill: #faecd7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-22"
|
||||
onclick="(()=>{console.log('node 22')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="220.79649" y="394.869471" style="fill: #fbead2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-23"
|
||||
onclick="(()=>{console.log('node 23')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="344.302973" y="166.290216" style="fill: #feddaf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-24"
|
||||
onclick="(()=>{console.log('node 24')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="241.321249" y="255.242558" style="fill: #c3c0dd" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-25"
|
||||
onclick="(()=>{console.log('node 25')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="257.120877" y="296.156882" style="fill: #f9f0e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-26"
|
||||
onclick="(()=>{console.log('node 26')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="207.563253" y="203.013335" style="fill: #fbebd5" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-27"
|
||||
onclick="(()=>{console.log('node 27')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="453.314054" y="329.209099" style="fill: #fdc47b" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-28"
|
||||
onclick="(()=>{console.log('node 28')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="350.123561" y="336.312105" style="fill: #fbe9cf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-29"
|
||||
onclick="(()=>{console.log('node 29')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="287.180764" y="309.56402" style="fill: #f7f6f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-30"
|
||||
onclick="(()=>{console.log('node 30')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="287.429936" y="148.935339" style="fill: #fbebd5" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-31"
|
||||
onclick="(()=>{console.log('node 31')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="281.878613" y="277.846944" style="fill: #d1d1e6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-32"
|
||||
onclick="(()=>{console.log('node 32')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="300.632415" y="433.812968" style="fill: #fde2bb" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-33"
|
||||
onclick="(()=>{console.log('node 33')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="216.196468" y="317.113868" style="fill: #dddfed" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-34"
|
||||
onclick="(()=>{console.log('node 34')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="419.504487" y="334.14189" style="fill: #fdc57f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-35"
|
||||
onclick="(()=>{console.log('node 35')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="321.071206" y="134.027026" style="fill: #fee0b6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-36"
|
||||
onclick="(()=>{console.log('node 36')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="366.514998" y="474.152727" style="fill: #fdbd6e" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-37"
|
||||
onclick="(()=>{console.log('node 37')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="208.453065" y="361.465389" style="fill: #cccbe3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-38"
|
||||
onclick="(()=>{console.log('node 38')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="236.649711" y="206.251683" style="fill: #faecd7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-39"
|
||||
onclick="(()=>{console.log('node 39')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="298.808991" y="236.296491" style="fill: #fdc57f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-40"
|
||||
onclick="(()=>{console.log('node 40')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="181.944484" y="318.835753" style="fill: #f9f0e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-41"
|
||||
onclick="(()=>{console.log('node 41')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="367.134249" y="434.454954" style="fill: #f6f6f7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-42"
|
||||
onclick="(()=>{console.log('node 42')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="422.276969" y="403.842356" style="fill: #fdbf72" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-43"
|
||||
onclick="(()=>{console.log('node 43')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="321.091057" y="403.95329" style="fill: #f8f5f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-44"
|
||||
onclick="(()=>{console.log('node 44')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="162.445269" y="355.484449" style="fill: #eaebf2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-45"
|
||||
onclick="(()=>{console.log('node 45')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="341.073251" y="272.287852" style="fill: #f6aa4f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-46"
|
||||
onclick="(()=>{console.log('node 46')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="389.299516" y="416.267064" style="fill: #de8013" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-47"
|
||||
onclick="(()=>{console.log('node 47')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="213.931911" y="434.927829" style="fill: #fbb55e" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-48"
|
||||
onclick="(()=>{console.log('node 48')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="436.061109" y="363.566053" style="fill: #ebecf3" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-0" onclick="(()=>{console.log('text 0 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(379.30079 338.694041) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-31"
|
||||
d="M 750 831 L 1813 831 L 1813 3847 L 722 3622 L 722 4441 L 1806 4666 L 2950 4666 L 2950 831 L 4013 831 L 4013 0 L 750 0 L 750 831 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-1" onclick="(()=>{console.log('text 1 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(389.387885 299.228722) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-33"
|
||||
d="M 2981 2516 Q 3453 2394 3698 2092 Q 3944 1791 3944 1325 Q 3944 631 3412 270 Q 2881 -91 1863 -91 Q 1503 -91 1142 -33 Q 781 25 428 141 L 428 1069 Q 766 900 1098 814 Q 1431 728 1753 728 Q 2231 728 2486 893 Q 2741 1059 2741 1369 Q 2741 1688 2480 1852 Q 2219 2016 1709 2016 L 1228 2016 L 1228 2791 L 1734 2791 Q 2188 2791 2409 2933 Q 2631 3075 2631 3366 Q 2631 3634 2415 3781 Q 2200 3928 1806 3928 Q 1516 3928 1219 3862 Q 922 3797 628 3669 L 628 4550 Q 984 4650 1334 4700 Q 1684 4750 2022 4750 Q 2931 4750 3382 4451 Q 3834 4153 3834 3553 Q 3834 3144 3618 2883 Q 3403 2622 2981 2516 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-2" onclick="(()=>{console.log('text 2 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(391.41758 187.661092) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-35"
|
||||
d="M 678 4666 L 3669 4666 L 3669 3781 L 1638 3781 L 1638 3059 Q 1775 3097 1914 3117 Q 2053 3138 2203 3138 Q 3056 3138 3531 2711 Q 4006 2284 4006 1522 Q 4006 766 3489 337 Q 2972 -91 2053 -91 Q 1656 -91 1267 -14 Q 878 63 494 219 L 494 1166 Q 875 947 1217 837 Q 1559 728 1863 728 Q 2300 728 2551 942 Q 2803 1156 2803 1522 Q 2803 1891 2551 2103 Q 2300 2316 1863 2316 Q 1603 2316 1309 2248 Q 1016 2181 678 2041 L 678 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-3" onclick="(()=>{console.log('text 3 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(162.43958 249.864438) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-36"
|
||||
d="M 2316 2303 Q 2000 2303 1842 2098 Q 1684 1894 1684 1484 Q 1684 1075 1842 870 Q 2000 666 2316 666 Q 2634 666 2792 870 Q 2950 1075 2950 1484 Q 2950 1894 2792 2098 Q 2634 2303 2316 2303 z M 3803 4544 L 3803 3681 Q 3506 3822 3243 3889 Q 2981 3956 2731 3956 Q 2194 3956 1894 3657 Q 1594 3359 1544 2772 Q 1750 2925 1990 3001 Q 2231 3078 2516 3078 Q 3231 3078 3670 2659 Q 4109 2241 4109 1563 Q 4109 813 3618 361 Q 3128 -91 2303 -91 Q 1394 -91 895 523 Q 397 1138 397 2266 Q 397 3422 980 4083 Q 1563 4744 2578 4744 Q 2900 4744 3203 4694 Q 3506 4644 3803 4544 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-4" onclick="(()=>{console.log('text 4 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.71135 398.663421) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-39"
|
||||
d="M 641 103 L 641 966 Q 928 831 1190 764 Q 1453 697 1709 697 Q 2247 697 2547 995 Q 2847 1294 2900 1881 Q 2688 1725 2447 1647 Q 2206 1569 1925 1569 Q 1209 1569 770 1986 Q 331 2403 331 3084 Q 331 3838 820 4291 Q 1309 4744 2131 4744 Q 3044 4744 3544 4128 Q 4044 3513 4044 2388 Q 4044 1231 3459 570 Q 2875 -91 1856 -91 Q 1528 -91 1228 -42 Q 928 6 641 103 z M 2125 2350 Q 2441 2350 2600 2554 Q 2759 2759 2759 3169 Q 2759 3575 2600 3781 Q 2441 3988 2125 3988 Q 1809 3988 1650 3781 Q 1491 3575 1491 3169 Q 1491 2759 1650 2554 Q 1809 2350 2125 2350 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-5" onclick="(()=>{console.log('text 5 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(283.349879 117.767448) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-6" onclick="(()=>{console.log('text 6 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(301.375385 350.124742) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-32"
|
||||
d="M 1844 884 L 3897 884 L 3897 0 L 506 0 L 506 884 L 2209 2388 Q 2438 2594 2547 2791 Q 2656 2988 2656 3200 Q 2656 3528 2436 3728 Q 2216 3928 1850 3928 Q 1569 3928 1234 3808 Q 900 3688 519 3450 L 519 4475 Q 925 4609 1322 4679 Q 1719 4750 2100 4750 Q 2938 4750 3402 4381 Q 3866 4013 3866 3353 Q 3866 2972 3669 2642 Q 3472 2313 2841 1759 L 1844 884 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-7" onclick="(()=>{console.log('text 7 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(388.046698 374.967662) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-8" onclick="(()=>{console.log('text 8 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(303.363625 192.113337) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-30"
|
||||
d="M 2944 2338 Q 2944 3213 2780 3570 Q 2616 3928 2228 3928 Q 1841 3928 1675 3570 Q 1509 3213 1509 2338 Q 1509 1453 1675 1090 Q 1841 728 2228 728 Q 2613 728 2778 1090 Q 2944 1453 2944 2338 z M 4147 2328 Q 4147 1169 3647 539 Q 3147 -91 2228 -91 Q 1306 -91 806 539 Q 306 1169 306 2328 Q 306 3491 806 4120 Q 1306 4750 2228 4750 Q 3147 4750 3647 4120 Q 4147 3491 4147 2328 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-9" onclick="(()=>{console.log('text 9 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(205.455661 289.031269) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-10" onclick="(()=>{console.log('text 10 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(318.115269 306.990997) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-11" onclick="(()=>{console.log('text 11 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(459.755142 293.508014) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-12" onclick="(()=>{console.log('text 12 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(161.065175 271.9803) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-38"
|
||||
d="M 2228 2088 Q 1891 2088 1709 1903 Q 1528 1719 1528 1375 Q 1528 1031 1709 848 Q 1891 666 2228 666 Q 2563 666 2741 848 Q 2919 1031 2919 1375 Q 2919 1722 2741 1905 Q 2563 2088 2228 2088 z M 1350 2484 Q 925 2613 709 2878 Q 494 3144 494 3541 Q 494 4131 934 4440 Q 1375 4750 2228 4750 Q 3075 4750 3515 4442 Q 3956 4134 3956 3541 Q 3956 3144 3739 2878 Q 3522 2613 3097 2484 Q 3572 2353 3814 2058 Q 4056 1763 4056 1313 Q 4056 619 3595 264 Q 3134 -91 2228 -91 Q 1319 -91 855 264 Q 391 619 391 1313 Q 391 1763 633 2058 Q 875 2353 1350 2484 z M 1631 3419 Q 1631 3141 1786 2991 Q 1941 2841 2228 2841 Q 2509 2841 2662 2991 Q 2816 3141 2816 3419 Q 2816 3697 2662 3845 Q 2509 3994 2228 3994 Q 1941 3994 1786 3844 Q 1631 3694 1631 3419 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-13" onclick="(()=>{console.log('text 13 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(244.173583 110.918523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-14" onclick="(()=>{console.log('text 14 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(233.467625 161.664737) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-15" onclick="(()=>{console.log('text 15 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(270.491369 193.884188) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-16" onclick="(()=>{console.log('text 16 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(181.971765 226.625792) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-34"
|
||||
d="M 2356 3675 L 1038 1722 L 2356 1722 L 2356 3675 z M 2156 4666 L 3494 4666 L 3494 1722 L 4159 1722 L 4159 850 L 3494 850 L 3494 0 L 2356 0 L 2356 850 L 288 850 L 288 1881 L 2156 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-17" onclick="(()=>{console.log('text 17 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(225.063335 351.712921) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-37"
|
||||
d="M 428 4666 L 3944 4666 L 3944 3988 L 2125 0 L 953 0 L 2675 3781 L 428 3781 L 428 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-18" onclick="(()=>{console.log('text 18 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(113.946108 355.813803) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-19" onclick="(()=>{console.log('text 19 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(150.275227 403.77299) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-20" onclick="(()=>{console.log('text 20 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(206.056148 247.886269) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-21" onclick="(()=>{console.log('text 21 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(256.219988 226.341346) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-22" onclick="(()=>{console.log('text 22 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(212.447115 398.180721) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-23" onclick="(()=>{console.log('text 23 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(335.953598 169.601466) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-24" onclick="(()=>{console.log('text 24 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(232.971874 258.553808) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-25" onclick="(()=>{console.log('text 25 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(248.771502 299.468132) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-26" onclick="(()=>{console.log('text 26 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(199.213878 206.324585) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-27" onclick="(()=>{console.log('text 27 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(444.964679 332.520349) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-28" onclick="(()=>{console.log('text 28 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(341.774186 339.623355) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-29" onclick="(()=>{console.log('text 29 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(278.831389 312.87527) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-30" onclick="(()=>{console.log('text 30 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(279.080561 152.246589) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-31" onclick="(()=>{console.log('text 31 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(273.529238 281.158194) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-32" onclick="(()=>{console.log('text 32 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(292.28304 437.124218) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-33" onclick="(()=>{console.log('text 33 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(207.847093 320.425118) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-34" onclick="(()=>{console.log('text 34 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(411.155112 337.45314) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-35" onclick="(()=>{console.log('text 35 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(312.721831 137.338276) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-36" onclick="(()=>{console.log('text 36 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.165623 477.463977) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-37" onclick="(()=>{console.log('text 37 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(200.10369 364.776639) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-38" onclick="(()=>{console.log('text 38 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(228.300336 209.562933) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-39" onclick="(()=>{console.log('text 39 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(290.459616 239.607741) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-40" onclick="(()=>{console.log('text 40 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(173.595109 322.147003) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-41" onclick="(()=>{console.log('text 41 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.784874 437.766204) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-42" onclick="(()=>{console.log('text 42 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(413.927594 407.153606) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-43" onclick="(()=>{console.log('text 43 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(312.741682 407.26454) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-44" onclick="(()=>{console.log('text 44 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(154.095894 358.795699) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-45" onclick="(()=>{console.log('text 45 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(332.723876 275.599102) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-46" onclick="(()=>{console.log('text 46 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(380.950141 419.578314) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-47" onclick="(()=>{console.log('text 47 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(205.582536 438.239079) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-48" onclick="(()=>{console.log('text 48 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(423.537047 366.877303) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="139.160156" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pd42c8a995e">
|
||||
<rect x="85.985534" y="69.12" width="418.428931" height="443.52" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 54 KiB |
@ -21,8 +21,8 @@
|
||||
{
|
||||
"name": "configArea",
|
||||
"options": {
|
||||
"detectors": ["dio", "harvest", "crepe", "crepe_full", "crepe_tiny", "rmvpe", "rmvpe_onnx"],
|
||||
"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]
|
||||
"detectors": ["dio", "harvest", "crepe", "crepe_full", "crepe_tiny", "rmvpe", "rmvpe_onnx", "fcpe"],
|
||||
"inputChunkNums": [1, 2, 4, 6, 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, 4096, 8192, 16384]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
1
client/demo/dist/assets/gui_settings/edition_web.txt
vendored
Normal file
1
client/demo/dist/assets/gui_settings/edition_web.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
web
|
2
client/demo/dist/index.js
vendored
2
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
4
client/demo/dist/index.js.LICENSE.txt
vendored
4
client/demo/dist/index.js.LICENSE.txt
vendored
@ -1,5 +1,9 @@
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/*!**********************!*\
|
||||
!*** ./src/index.ts ***!
|
||||
\**********************/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
|
3902
client/demo/package-lock.json
generated
3902
client/demo/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,14 @@
|
||||
"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 && 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"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"____ comment ____": "ウェブバージョンのスクリプト",
|
||||
"clean:web": "rimraf dist_web/",
|
||||
"webpack:web:prod": "npx webpack --config webpack_web.prod.js && copy .\\public\\info_web .\\dist_web\\info && copy .\\public\\assets\\gui_settings\\edition_web.txt .\\dist_web\\assets\\gui_settings\\edition.txt",
|
||||
"webpack:web:dev": "npx webpack --config webpack_web.dev.js && copy .\\public\\info_web .\\dist_web\\info && copy .\\public\\assets\\gui_settings\\edition_web.txt .\\dist_web\\assets\\gui_settings\\edition.txt",
|
||||
"build:web:prod": "npm-run-all clean:web webpack:web:prod",
|
||||
"build:web:dev": "npm-run-all clean:web webpack:web:dev",
|
||||
"start:web": "webpack-dev-server --config webpack_web.dev.js"
|
||||
},
|
||||
"keywords": [
|
||||
"voice conversion"
|
||||
@ -21,46 +28,51 @@
|
||||
"author": "wataru.okada@flect.co.jp",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.20",
|
||||
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||
"@babel/preset-env": "^7.22.20",
|
||||
"@babel/preset-react": "^7.22.15",
|
||||
"@babel/preset-typescript": "^7.22.15",
|
||||
"@types/node": "^20.6.3",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"@babel/core": "^7.24.0",
|
||||
"@babel/plugin-transform-runtime": "^7.24.0",
|
||||
"@babel/preset-env": "^7.24.0",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@types/node": "^20.11.21",
|
||||
"@types/react": "^18.2.60",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^6.10.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"html-loader": "^5.0.0",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"prettier": "^3.0.3",
|
||||
"rimraf": "^5.0.1",
|
||||
"style-loader": "^3.3.3",
|
||||
"ts-loader": "^9.4.4",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^5.0.5",
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.2.2",
|
||||
"webpack": "^5.88.2",
|
||||
"typescript": "^5.3.3",
|
||||
"webpack": "^5.90.3",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
"webpack-dev-server": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dannadori/voice-changer-client-js": "^1.0.172",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@alexanderolsen/libsamplerate-js": "^2.1.1",
|
||||
"@dannadori/voice-changer-client-js": "^1.0.182",
|
||||
"@dannadori/voice-changer-js": "^1.0.2",
|
||||
"@dannadori/worker-manager": "^1.0.20",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"protobufjs": "^7.2.5",
|
||||
"@tensorflow/tfjs": "^4.17.0",
|
||||
"onnxruntime-web": "^1.17.1",
|
||||
"protobufjs": "^7.2.6",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
|
928
client/demo/public/assets/beatrice/female-clickable.svg
Normal file
928
client/demo/public/assets/beatrice/female-clickable.svg
Normal file
@ -0,0 +1,928 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:ns2="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="100 60 420 450" version="1.1">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<ns2:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2023-11-19T11:21:56.358384</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<ns2:Agent>
|
||||
<dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title>
|
||||
</ns2:Agent>
|
||||
</dc:creator>
|
||||
</ns2:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
* {
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: butt
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.beatrice-node-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer:hover {
|
||||
stroke: gray;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer-selected {
|
||||
stroke: #ef6767c2;
|
||||
stroke-width: 3
|
||||
}
|
||||
|
||||
.beatrice-text-pointer {
|
||||
cursor: pointer;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.beatrice-text-pointer:hover {
|
||||
/* ホバー時のスタイルは既に設定されたスタイルと異なる特定の属性を変更することができます。 */
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 576 L 576 576 L 576 0 L 0 0 z " style="fill: #ffffff" />
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="LineCollection_1">
|
||||
<path d="M 403.96157 149.258085 L 366.630583 148.159991 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 372.120414 365.421971 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 416.760989 346.999139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.547407 371.476481 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 258.035169 326.134244 L 298.859694 332.465911 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 167.453327 366.897955 L 203.987537 347.931194 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 436.352807 416.173738 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 417.560846 259.464346 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 391.514336 242.048236 L 424.070309 219.021704 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 205.541044 459.711101 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 160.44225 292.540336 L 167.396334 325.961848 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.679012 107.607273 L 366.630583 148.159991 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 325.345004 219.195921 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 325.345004 219.195921 L 297.530501 194.55124 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 363.075301 201.701937 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 363.075301 201.701937 L 341.462109 170.414842 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 366.630583 148.159991 L 341.462109 170.414842 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 167.396334 325.961848 L 203.987537 347.931194 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 262.111309 181.887977 L 297.530501 194.55124 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 189.293496 262.735141 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 277.95603 462.622539 L 293.230217 421.393405 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 333.932593 269.342364 L 301.9174 258.124913 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 333.932593 269.342364 L 355.734145 235.68791 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 334.666605 338.097578 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 203.987537 347.931194 L 242.811958 354.082183 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 276.198518 388.99868 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 298.859694 332.465911 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 288.059985 363.924972 L 242.811958 354.082183 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 276.198518 388.99868 L 293.230217 421.393405 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 293.230217 421.393405 L 309.530924 454.827332 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 293.230217 421.393405 L 260.004712 426.426278 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 423.853712 378.321354 L 404.238335 402.754731 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 205.214352 217.066163 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 154.047193 423.153273 L 193.933786 408.004355 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 298.859694 332.465911 L 277.79477 306.980241 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 277.79477 306.980241 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 228.689744 409.959215 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 260.004712 426.426278 L 261.506403 474.152727 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 301.9174 258.124913 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 254.897329 263.033159 L 282.261978 282.779534 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 254.897329 263.033159 L 222.122563 261.416721 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 321.267463 403.021207 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 320.07566 368.578481 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 372.120414 365.421971 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 345.750267 380.131711 L 351.226176 419.342667 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 404.238335 402.754731 L 400.607869 434.730447 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 193.933786 408.004355 L 230.303076 436.148139 " clip-path="url(#pe3de578e26)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
</g>
|
||||
<g id="PathCollection_1">
|
||||
<defs>
|
||||
<path id="C0_0_b0ffb3bf4a"
|
||||
d="M 0 11.18034 C 2.965061 11.18034 5.80908 10.002309 7.905694 7.905694 C 10.002309 5.80908 11.18034 2.965061 11.18034 -0 C 11.18034 -2.965061 10.002309 -5.80908 7.905694 -7.905694 C 5.80908 -10.002309 2.965061 -11.18034 0 -11.18034 C -2.965061 -11.18034 -5.80908 -10.002309 -7.905694 -7.905694 C -10.002309 -5.80908 -11.18034 -2.965061 -11.18034 0 C -11.18034 2.965061 -10.002309 5.80908 -7.905694 7.905694 C -5.80908 10.002309 -2.965061 11.18034 0 11.18034 z " />
|
||||
</defs>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-0"
|
||||
onclick="(()=>{console.log('node 0')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="403.96157" y="149.258085" style="fill: #e7f5d2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-1"
|
||||
onclick="(()=>{console.log('node 1')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="396.547407" y="371.476481" style="fill: #fbe8f2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-2"
|
||||
onclick="(()=>{console.log('node 2')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="258.035169" y="326.134244" style="fill: #cfebaa" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-3"
|
||||
onclick="(()=>{console.log('node 3')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="167.453327" y="366.897955" style="fill: #f1f6e8" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-4"
|
||||
onclick="(()=>{console.log('node 4')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="436.352807" y="416.173738" style="fill: #e89ac6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-5"
|
||||
onclick="(()=>{console.log('node 5')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="391.514336" y="242.048236" style="fill: #f3bcdd" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-6"
|
||||
onclick="(()=>{console.log('node 6')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="205.541044" y="459.711101" style="fill: #fbd9ec" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-7"
|
||||
onclick="(()=>{console.log('node 7')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="160.44225" y="292.540336" style="fill: #9ed067" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-8"
|
||||
onclick="(()=>{console.log('node 8')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="424.070309" y="219.021704" style="fill: #e1f3c7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-9"
|
||||
onclick="(()=>{console.log('node 9')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="345.679012" y="107.607273" style="fill: #d0ecad" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-10"
|
||||
onclick="(()=>{console.log('node 10')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="325.345004" y="219.195921" style="fill: #eff6e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-11"
|
||||
onclick="(()=>{console.log('node 11')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="363.075301" y="201.701937" style="fill: #f9f0f5" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-12"
|
||||
onclick="(()=>{console.log('node 12')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="366.630583" y="148.159991" style="fill: #ebf6dc" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-13"
|
||||
onclick="(()=>{console.log('node 13')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="341.462109" y="170.414842" style="fill: #fad6ea" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-14"
|
||||
onclick="(()=>{console.log('node 14')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="167.396334" y="325.961848" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-15"
|
||||
onclick="(()=>{console.log('node 15')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="262.111309" y="181.887977" style="fill: #e9f5d6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-16"
|
||||
onclick="(()=>{console.log('node 16')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="189.293496" y="262.735141" style="fill: #fce5f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-17"
|
||||
onclick="(()=>{console.log('node 17')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="277.95603" y="462.622539" style="fill: #c4e699" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-18"
|
||||
onclick="(()=>{console.log('node 18')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="333.932593" y="269.342364" style="fill: #f8f4f6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-19"
|
||||
onclick="(()=>{console.log('node 19')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="416.760989" y="346.999139" style="fill: #eef6e2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-20"
|
||||
onclick="(()=>{console.log('node 20')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="334.666605" y="338.097578" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-21"
|
||||
onclick="(()=>{console.log('node 21')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="203.987537" y="347.931194" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-22"
|
||||
onclick="(()=>{console.log('node 22')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="288.059985" y="363.924972" style="fill: #ddf1c1" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-23"
|
||||
onclick="(()=>{console.log('node 23')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="276.198518" y="388.99868" style="fill: #f5f7f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-24"
|
||||
onclick="(()=>{console.log('node 24')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="293.230217" y="421.393405" style="fill: #f3f7ef" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-25"
|
||||
onclick="(()=>{console.log('node 25')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="423.853712" y="378.321354" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-26"
|
||||
onclick="(()=>{console.log('node 26')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="205.214352" y="217.066163" style="fill: #e7f5d2" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-27"
|
||||
onclick="(()=>{console.log('node 27')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="242.811958" y="354.082183" style="fill: #d2ecb0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-28"
|
||||
onclick="(()=>{console.log('node 28')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="154.047193" y="423.153273" style="fill: #e6f5d0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-29"
|
||||
onclick="(()=>{console.log('node 29')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="298.859694" y="332.465911" style="fill: #ecf6de" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-30"
|
||||
onclick="(()=>{console.log('node 30')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="277.79477" y="306.980241" style="fill: #eaf5d9" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-31"
|
||||
onclick="(()=>{console.log('node 31')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="260.004712" y="426.426278" style="fill: #f9f1f5" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-32"
|
||||
onclick="(()=>{console.log('node 32')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="301.9174" y="258.124913" style="fill: #dbf0bf" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-33"
|
||||
onclick="(()=>{console.log('node 33')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="254.897329" y="263.033159" style="fill: #eff6e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-34"
|
||||
onclick="(()=>{console.log('node 34')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="321.267463" y="403.021207" style="fill: #d0ecad" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-35"
|
||||
onclick="(()=>{console.log('node 35')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="345.750267" y="380.131711" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-36"
|
||||
onclick="(()=>{console.log('node 36')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="404.238335" y="402.754731" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-37"
|
||||
onclick="(()=>{console.log('node 37')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="355.734145" y="235.68791" style="fill: #f9eef4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-38"
|
||||
onclick="(()=>{console.log('node 38')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="193.933786" y="408.004355" style="fill: #f0f6e7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-39"
|
||||
onclick="(()=>{console.log('node 39')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="297.530501" y="194.55124" style="fill: #f3f6ed" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-40"
|
||||
onclick="(()=>{console.log('node 40')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="320.07566" y="368.578481" style="fill: #dbf0bf" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-41"
|
||||
onclick="(()=>{console.log('node 41')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="228.689744" y="409.959215" style="fill: #f9eff4" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-42"
|
||||
onclick="(()=>{console.log('node 42')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="351.226176" y="419.342667" style="fill: #cfebaa" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-43"
|
||||
onclick="(()=>{console.log('node 43')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="372.120414" y="365.421971" style="fill: #f7f6f7" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-44"
|
||||
onclick="(()=>{console.log('node 44')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="230.303076" y="436.148139" style="fill: #f8cee6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-45"
|
||||
onclick="(()=>{console.log('node 45')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="261.506403" y="474.152727" style="fill: #e6f5d0" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-46"
|
||||
onclick="(()=>{console.log('node 46')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="417.560846" y="259.464346" style="fill: #b7e085" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-47"
|
||||
onclick="(()=>{console.log('node 47')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="400.607869" y="434.730447" style="fill: #f8cee6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-48"
|
||||
onclick="(()=>{console.log('node 48')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="282.261978" y="282.779534" style="fill: #d6eeb6" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-49"
|
||||
onclick="(()=>{console.log('node 49')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="222.122563" y="261.416721" style="fill: #edf6df" />
|
||||
</g>
|
||||
<g clip-path="url(#pe3de578e26)" id="beatrice-node-female-50"
|
||||
onclick="(()=>{console.log('node 50')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_b0ffb3bf4a" x="309.530924" y="454.827332" style="fill: #f9eef4" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-0" onclick="(()=>{console.log('text 0 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(399.786883 152.569335) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-32"
|
||||
d="M 1844 884 L 3897 884 L 3897 0 L 506 0 L 506 884 L 2209 2388 Q 2438 2594 2547 2791 Q 2656 2988 2656 3200 Q 2656 3528 2436 3728 Q 2216 3928 1850 3928 Q 1569 3928 1234 3808 Q 900 3688 519 3450 L 519 4475 Q 925 4609 1322 4679 Q 1719 4750 2100 4750 Q 2938 4750 3402 4381 Q 3866 4013 3866 3353 Q 3866 2972 3669 2642 Q 3472 2313 2841 1759 L 1844 884 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-1" onclick="(()=>{console.log('text 1 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(392.37272 374.787731) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-34"
|
||||
d="M 2356 3675 L 1038 1722 L 2356 1722 L 2356 3675 z M 2156 4666 L 3494 4666 L 3494 1722 L 4159 1722 L 4159 850 L 3494 850 L 3494 0 L 2356 0 L 2356 850 L 288 850 L 288 1881 L 2156 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-2" onclick="(()=>{console.log('text 2 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.860482 329.445494) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-37"
|
||||
d="M 428 4666 L 3944 4666 L 3944 3988 L 2125 0 L 953 0 L 2675 3781 L 428 3781 L 428 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-3" onclick="(()=>{console.log('text 3 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(163.27864 370.209205) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-38"
|
||||
d="M 2228 2088 Q 1891 2088 1709 1903 Q 1528 1719 1528 1375 Q 1528 1031 1709 848 Q 1891 666 2228 666 Q 2563 666 2741 848 Q 2919 1031 2919 1375 Q 2919 1722 2741 1905 Q 2563 2088 2228 2088 z M 1350 2484 Q 925 2613 709 2878 Q 494 3144 494 3541 Q 494 4131 934 4440 Q 1375 4750 2228 4750 Q 3075 4750 3515 4442 Q 3956 4134 3956 3541 Q 3956 3144 3739 2878 Q 3522 2613 3097 2484 Q 3572 2353 3814 2058 Q 4056 1763 4056 1313 Q 4056 619 3595 264 Q 3134 -91 2228 -91 Q 1319 -91 855 264 Q 391 619 391 1313 Q 391 1763 633 2058 Q 875 2353 1350 2484 z M 1631 3419 Q 1631 3141 1786 2991 Q 1941 2841 2228 2841 Q 2509 2841 2662 2991 Q 2816 3141 2816 3419 Q 2816 3697 2662 3845 Q 2509 3994 2228 3994 Q 1941 3994 1786 3844 Q 1631 3694 1631 3419 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-4" onclick="(()=>{console.log('text 4 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(428.003432 419.484988) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-31"
|
||||
d="M 750 831 L 1813 831 L 1813 3847 L 722 3622 L 722 4441 L 1806 4666 L 2950 4666 L 2950 831 L 4013 831 L 4013 0 L 750 0 L 750 831 z "
|
||||
transform="scale(0.015625)" />
|
||||
<path id="DejaVuSans-Bold-30"
|
||||
d="M 2944 2338 Q 2944 3213 2780 3570 Q 2616 3928 2228 3928 Q 1841 3928 1675 3570 Q 1509 3213 1509 2338 Q 1509 1453 1675 1090 Q 1841 728 2228 728 Q 2613 728 2778 1090 Q 2944 1453 2944 2338 z M 4147 2328 Q 4147 1169 3647 539 Q 3147 -91 2228 -91 Q 1306 -91 806 539 Q 306 1169 306 2328 Q 306 3491 806 4120 Q 1306 4750 2228 4750 Q 3147 4750 3647 4120 Q 4147 3491 4147 2328 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-5" onclick="(()=>{console.log('text 5 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(383.164961 245.359486) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-6" onclick="(()=>{console.log('text 6 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(197.191669 463.022351) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-35"
|
||||
d="M 678 4666 L 3669 4666 L 3669 3781 L 1638 3781 L 1638 3059 Q 1775 3097 1914 3117 Q 2053 3138 2203 3138 Q 3056 3138 3531 2711 Q 4006 2284 4006 1522 Q 4006 766 3489 337 Q 2972 -91 2053 -91 Q 1656 -91 1267 -14 Q 878 63 494 219 L 494 1166 Q 875 947 1217 837 Q 1559 728 1863 728 Q 2300 728 2551 942 Q 2803 1156 2803 1522 Q 2803 1891 2551 2103 Q 2300 2316 1863 2316 Q 1603 2316 1309 2248 Q 1016 2181 678 2041 L 678 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-7" onclick="(()=>{console.log('text 7 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(152.092875 295.851586) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-36"
|
||||
d="M 2316 2303 Q 2000 2303 1842 2098 Q 1684 1894 1684 1484 Q 1684 1075 1842 870 Q 2000 666 2316 666 Q 2634 666 2792 870 Q 2950 1075 2950 1484 Q 2950 1894 2792 2098 Q 2634 2303 2316 2303 z M 3803 4544 L 3803 3681 Q 3506 3822 3243 3889 Q 2981 3956 2731 3956 Q 2194 3956 1894 3657 Q 1594 3359 1544 2772 Q 1750 2925 1990 3001 Q 2231 3078 2516 3078 Q 3231 3078 3670 2659 Q 4109 2241 4109 1563 Q 4109 813 3618 361 Q 3128 -91 2303 -91 Q 1394 -91 895 523 Q 397 1138 397 2266 Q 397 3422 980 4083 Q 1563 4744 2578 4744 Q 2900 4744 3203 4694 Q 3506 4644 3803 4544 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-8" onclick="(()=>{console.log('text 8 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(415.720934 222.332954) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-9" onclick="(()=>{console.log('text 9 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(337.329637 110.918523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-10" onclick="(()=>{console.log('text 10 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(316.995629 222.507171) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-39"
|
||||
d="M 641 103 L 641 966 Q 928 831 1190 764 Q 1453 697 1709 697 Q 2247 697 2547 995 Q 2847 1294 2900 1881 Q 2688 1725 2447 1647 Q 2206 1569 1925 1569 Q 1209 1569 770 1986 Q 331 2403 331 3084 Q 331 3838 820 4291 Q 1309 4744 2131 4744 Q 3044 4744 3544 4128 Q 4044 3513 4044 2388 Q 4044 1231 3459 570 Q 2875 -91 1856 -91 Q 1528 -91 1228 -42 Q 928 6 641 103 z M 2125 2350 Q 2441 2350 2600 2554 Q 2759 2759 2759 3169 Q 2759 3575 2600 3781 Q 2441 3988 2125 3988 Q 1809 3988 1650 3781 Q 1491 3575 1491 3169 Q 1491 2759 1650 2554 Q 1809 2350 2125 2350 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-11" onclick="(()=>{console.log('text 11 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(354.725926 205.013187) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-12" onclick="(()=>{console.log('text 12 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(358.281208 151.471241) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-13" onclick="(()=>{console.log('text 13 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(333.112734 173.726092) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-14" onclick="(()=>{console.log('text 14 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(159.046959 329.273098) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-15" onclick="(()=>{console.log('text 15 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.761934 185.199227) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-16" onclick="(()=>{console.log('text 16 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(180.944121 266.046391) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-33"
|
||||
d="M 2981 2516 Q 3453 2394 3698 2092 Q 3944 1791 3944 1325 Q 3944 631 3412 270 Q 2881 -91 1863 -91 Q 1503 -91 1142 -33 Q 781 25 428 141 L 428 1069 Q 766 900 1098 814 Q 1431 728 1753 728 Q 2231 728 2486 893 Q 2741 1059 2741 1369 Q 2741 1688 2480 1852 Q 2219 2016 1709 2016 L 1228 2016 L 1228 2791 L 1734 2791 Q 2188 2791 2409 2933 Q 2631 3075 2631 3366 Q 2631 3634 2415 3781 Q 2200 3928 1806 3928 Q 1516 3928 1219 3862 Q 922 3797 628 3669 L 628 4550 Q 984 4650 1334 4700 Q 1684 4750 2022 4750 Q 2931 4750 3382 4451 Q 3834 4153 3834 3553 Q 3834 3144 3618 2883 Q 3403 2622 2981 2516 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-17" onclick="(()=>{console.log('text 17 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(269.606655 465.933789) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-18" onclick="(()=>{console.log('text 18 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(325.583218 272.653614) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-19" onclick="(()=>{console.log('text 19 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(408.411614 350.310389) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-20" onclick="(()=>{console.log('text 20 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(326.31723 341.408828) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-21" onclick="(()=>{console.log('text 21 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(195.638162 351.242444) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-22" onclick="(()=>{console.log('text 22 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(279.71061 367.236222) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-23" onclick="(()=>{console.log('text 23 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(267.849143 392.30993) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-24" onclick="(()=>{console.log('text 24 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(284.880842 424.704655) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-25" onclick="(()=>{console.log('text 25 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(415.504337 381.632604) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-26" onclick="(()=>{console.log('text 26 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(196.864977 220.377413) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-27" onclick="(()=>{console.log('text 27 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(234.462583 357.393433) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-28" onclick="(()=>{console.log('text 28 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(145.697818 426.464523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-29" onclick="(()=>{console.log('text 29 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(290.510319 335.777161) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-30" onclick="(()=>{console.log('text 30 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(269.445395 310.291491) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-31" onclick="(()=>{console.log('text 31 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(251.655337 429.737528) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-32" onclick="(()=>{console.log('text 32 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(293.568025 261.436163) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-33" onclick="(()=>{console.log('text 33 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(246.547954 266.344409) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-34" onclick="(()=>{console.log('text 34 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(312.918088 406.332457) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-35" onclick="(()=>{console.log('text 35 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(337.400892 383.442961) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-36" onclick="(()=>{console.log('text 36 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(395.88896 406.065981) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-37" onclick="(()=>{console.log('text 37 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(347.38477 238.99916) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-38" onclick="(()=>{console.log('text 38 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(185.584411 411.315605) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-39" onclick="(()=>{console.log('text 39 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(289.181126 197.86249) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-40" onclick="(()=>{console.log('text 40 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(311.726285 371.889731) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-41" onclick="(()=>{console.log('text 41 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(220.340369 413.270465) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-42" onclick="(()=>{console.log('text 42 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(342.876801 422.653917) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-43" onclick="(()=>{console.log('text 43 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(363.771039 368.733221) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-44" onclick="(()=>{console.log('text 44 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(221.953701 439.459389) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-45" onclick="(()=>{console.log('text 45 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(253.157028 477.463977) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-46" onclick="(()=>{console.log('text 46 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(409.211471 262.775596) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-47" onclick="(()=>{console.log('text 47 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(392.258494 438.041697) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-48" onclick="(()=>{console.log('text 48 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(273.912603 286.090784) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-49" onclick="(()=>{console.log('text 49 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(213.773188 264.727971) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-female-50" onclick="(()=>{console.log('text 50 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pe3de578e26)">
|
||||
|
||||
<g transform="translate(301.181549 458.138582) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pe3de578e26">
|
||||
<rect x="124.405104" y="69.12" width="341.589792" height="443.52" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 56 KiB |
898
client/demo/public/assets/beatrice/male-clickable.svg
Normal file
898
client/demo/public/assets/beatrice/male-clickable.svg
Normal file
@ -0,0 +1,898 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:ns2="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="100 60 420 450" version="1.1">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<ns2:Work>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2023-11-19T11:21:55.705408</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:creator>
|
||||
<ns2:Agent>
|
||||
<dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title>
|
||||
</ns2:Agent>
|
||||
</dc:creator>
|
||||
</ns2:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
* {
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: butt
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.beatrice-node-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer:hover {
|
||||
stroke: gray;
|
||||
}
|
||||
|
||||
.beatrice-node-pointer-selected {
|
||||
stroke: #ef6767c2;
|
||||
stroke-width: 3
|
||||
}
|
||||
|
||||
.beatrice-text-pointer {
|
||||
cursor: pointer;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.beatrice-text-pointer:hover {
|
||||
/* ホバー時のスタイルは既に設定されたスタイルと異なる特定の属性を変更することができます。 */
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 576 L 576 576 L 576 0 L 0 0 z " style="fill: #ffffff" />
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="LineCollection_1">
|
||||
<path d="M 383.475478 335.382791 L 350.123561 336.312105 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 383.475478 335.382791 L 393.562573 295.917472 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 383.475478 335.382791 L 396.396073 371.656412 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 395.592267 184.349842 L 344.302973 166.290216 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 166.614267 246.553188 L 214.405523 244.575019 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 389.299516 416.267064 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 367.134249 434.454954 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 396.396073 371.656412 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 362.886037 395.352171 L 321.091057 403.95329 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 291.699254 114.456198 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 309.72476 346.813492 L 326.464644 303.679747 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.396073 371.656412 L 422.276969 403.842356 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 396.396073 371.656412 L 419.504487 334.14189 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 278.840744 190.572938 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 311.713 188.802087 L 344.302973 166.290216 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 216.196468 317.113868 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 213.805036 285.720019 L 169.41455 268.66905 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 341.073251 272.287852 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 326.464644 303.679747 L 350.123561 336.312105 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 468.104517 290.196764 L 453.314054 329.209099 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 252.522958 107.607273 L 287.429936 148.935339 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 241.817 158.353487 L 278.840744 190.572938 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 278.840744 190.572938 L 264.569363 223.030096 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 190.32114 223.314542 L 214.405523 244.575019 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 233.41271 348.401671 L 216.196468 317.113868 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 122.295483 352.502553 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 158.624602 400.46174 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 214.405523 244.575019 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 214.405523 244.575019 L 207.563253 203.013335 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 298.808991 236.296491 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 241.321249 255.242558 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 264.569363 223.030096 L 236.649711 206.251683 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 220.79649 394.869471 L 213.931911 434.927829 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 220.79649 394.869471 L 208.453065 361.465389 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 241.321249 255.242558 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 257.120877 296.156882 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 453.314054 329.209099 L 419.504487 334.14189 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 287.180764 309.56402 L 281.878613 277.846944 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 287.429936 148.935339 L 321.071206 134.027026 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 300.632415 433.812968 L 321.091057 403.95329 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 216.196468 317.113868 L 181.944484 318.835753 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 216.196468 317.113868 L 208.453065 361.465389 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 419.504487 334.14189 L 436.061109 363.566053 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 366.514998 474.152727 L 367.134249 434.454954 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
<path d="M 208.453065 361.465389 L 162.445269 355.484449 " clip-path="url(#pd42c8a995e)"
|
||||
style="fill: none; stroke: #808080" />
|
||||
</g>
|
||||
<g id="PathCollection_1">
|
||||
<defs>
|
||||
<path id="C0_0_3858269516"
|
||||
d="M 0 11.18034 C 2.965061 11.18034 5.80908 10.002309 7.905694 7.905694 C 10.002309 5.80908 11.18034 2.965061 11.18034 -0 C 11.18034 -2.965061 10.002309 -5.80908 7.905694 -7.905694 C 5.80908 -10.002309 2.965061 -11.18034 0 -11.18034 C -2.965061 -11.18034 -5.80908 -10.002309 -7.905694 -7.905694 C -10.002309 -5.80908 -11.18034 -2.965061 -11.18034 0 C -11.18034 2.965061 -10.002309 5.80908 -7.905694 7.905694 C -5.80908 10.002309 -2.965061 11.18034 0 11.18034 z " />
|
||||
</defs>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-0" onclick="(()=>{console.log('node 0')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="383.475478" y="335.382791" style="fill: #fde2bb" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-1" onclick="(()=>{console.log('node 1')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="393.562573" y="295.917472" style="fill: #fdba68" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-2" onclick="(()=>{console.log('node 2')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="395.592267" y="184.349842" style="fill: #fbe9cf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-3" onclick="(()=>{console.log('node 3')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="166.614267" y="246.553188" style="fill: #7e70ab" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-4" onclick="(()=>{console.log('node 4')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="362.886037" y="395.352171" style="fill: #e8e9f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-5" onclick="(()=>{console.log('node 5')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="291.699254" y="114.456198" style="fill: #f9b158" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-6" onclick="(()=>{console.log('node 6')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="309.72476" y="346.813492" style="fill: #e4e5f0" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-7" onclick="(()=>{console.log('node 7')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="396.396073" y="371.656412" style="fill: #fdcc8c" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-8" onclick="(()=>{console.log('node 8')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="311.713" y="188.802087" style="fill: #fedeb3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-9" onclick="(()=>{console.log('node 9')})()"
|
||||
class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="213.805036" y="285.720019" style="fill: #bab5d7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-10"
|
||||
onclick="(()=>{console.log('node 10')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="326.464644" y="303.679747" style="fill: #eaebf2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-11"
|
||||
onclick="(()=>{console.log('node 11')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="468.104517" y="290.196764" style="fill: #f7f7f6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-12"
|
||||
onclick="(()=>{console.log('node 12')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="169.41455" y="268.66905" style="fill: #dfe1ee" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-13"
|
||||
onclick="(()=>{console.log('node 13')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="252.522958" y="107.607273" style="fill: #eff0f4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-14"
|
||||
onclick="(()=>{console.log('node 14')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="241.817" y="158.353487" style="fill: #e58a20" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-15"
|
||||
onclick="(()=>{console.log('node 15')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="278.840744" y="190.572938" style="fill: #fedbac" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-16"
|
||||
onclick="(()=>{console.log('node 16')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="190.32114" y="223.314542" style="fill: #dfe1ee" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-17"
|
||||
onclick="(()=>{console.log('node 17')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="233.41271" y="348.401671" style="fill: #c3c0dd" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-18"
|
||||
onclick="(()=>{console.log('node 18')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="122.295483" y="352.502553" style="fill: #fed8a6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-19"
|
||||
onclick="(()=>{console.log('node 19')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="158.624602" y="400.46174" style="fill: #f7f6f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-20"
|
||||
onclick="(()=>{console.log('node 20')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="214.405523" y="244.575019" style="fill: #f9f2e9" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-21"
|
||||
onclick="(()=>{console.log('node 21')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="264.569363" y="223.030096" style="fill: #faecd7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-22"
|
||||
onclick="(()=>{console.log('node 22')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="220.79649" y="394.869471" style="fill: #fbead2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-23"
|
||||
onclick="(()=>{console.log('node 23')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="344.302973" y="166.290216" style="fill: #feddaf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-24"
|
||||
onclick="(()=>{console.log('node 24')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="241.321249" y="255.242558" style="fill: #c3c0dd" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-25"
|
||||
onclick="(()=>{console.log('node 25')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="257.120877" y="296.156882" style="fill: #f9f0e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-26"
|
||||
onclick="(()=>{console.log('node 26')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="207.563253" y="203.013335" style="fill: #fbebd5" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-27"
|
||||
onclick="(()=>{console.log('node 27')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="453.314054" y="329.209099" style="fill: #fdc47b" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-28"
|
||||
onclick="(()=>{console.log('node 28')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="350.123561" y="336.312105" style="fill: #fbe9cf" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-29"
|
||||
onclick="(()=>{console.log('node 29')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="287.180764" y="309.56402" style="fill: #f7f6f3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-30"
|
||||
onclick="(()=>{console.log('node 30')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="287.429936" y="148.935339" style="fill: #fbebd5" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-31"
|
||||
onclick="(()=>{console.log('node 31')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="281.878613" y="277.846944" style="fill: #d1d1e6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-32"
|
||||
onclick="(()=>{console.log('node 32')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="300.632415" y="433.812968" style="fill: #fde2bb" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-33"
|
||||
onclick="(()=>{console.log('node 33')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="216.196468" y="317.113868" style="fill: #dddfed" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-34"
|
||||
onclick="(()=>{console.log('node 34')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="419.504487" y="334.14189" style="fill: #fdc57f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-35"
|
||||
onclick="(()=>{console.log('node 35')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="321.071206" y="134.027026" style="fill: #fee0b6" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-36"
|
||||
onclick="(()=>{console.log('node 36')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="366.514998" y="474.152727" style="fill: #fdbd6e" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-37"
|
||||
onclick="(()=>{console.log('node 37')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="208.453065" y="361.465389" style="fill: #cccbe3" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-38"
|
||||
onclick="(()=>{console.log('node 38')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="236.649711" y="206.251683" style="fill: #faecd7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-39"
|
||||
onclick="(()=>{console.log('node 39')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="298.808991" y="236.296491" style="fill: #fdc57f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-40"
|
||||
onclick="(()=>{console.log('node 40')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="181.944484" y="318.835753" style="fill: #f9f0e4" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-41"
|
||||
onclick="(()=>{console.log('node 41')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="367.134249" y="434.454954" style="fill: #f6f6f7" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-42"
|
||||
onclick="(()=>{console.log('node 42')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="422.276969" y="403.842356" style="fill: #fdbf72" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-43"
|
||||
onclick="(()=>{console.log('node 43')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="321.091057" y="403.95329" style="fill: #f8f5f1" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-44"
|
||||
onclick="(()=>{console.log('node 44')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="162.445269" y="355.484449" style="fill: #eaebf2" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-45"
|
||||
onclick="(()=>{console.log('node 45')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="341.073251" y="272.287852" style="fill: #f6aa4f" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-46"
|
||||
onclick="(()=>{console.log('node 46')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="389.299516" y="416.267064" style="fill: #de8013" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-47"
|
||||
onclick="(()=>{console.log('node 47')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="213.931911" y="434.927829" style="fill: #fbb55e" />
|
||||
</g>
|
||||
<g clip-path="url(#pd42c8a995e)" id="beatrice-node-male-48"
|
||||
onclick="(()=>{console.log('node 48')})()" class="beatrice-node-pointer">
|
||||
<use xlink:href="#C0_0_3858269516" x="436.061109" y="363.566053" style="fill: #ebecf3" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-0" onclick="(()=>{console.log('text 0 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(379.30079 338.694041) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-31"
|
||||
d="M 750 831 L 1813 831 L 1813 3847 L 722 3622 L 722 4441 L 1806 4666 L 2950 4666 L 2950 831 L 4013 831 L 4013 0 L 750 0 L 750 831 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-1" onclick="(()=>{console.log('text 1 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(389.387885 299.228722) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-33"
|
||||
d="M 2981 2516 Q 3453 2394 3698 2092 Q 3944 1791 3944 1325 Q 3944 631 3412 270 Q 2881 -91 1863 -91 Q 1503 -91 1142 -33 Q 781 25 428 141 L 428 1069 Q 766 900 1098 814 Q 1431 728 1753 728 Q 2231 728 2486 893 Q 2741 1059 2741 1369 Q 2741 1688 2480 1852 Q 2219 2016 1709 2016 L 1228 2016 L 1228 2791 L 1734 2791 Q 2188 2791 2409 2933 Q 2631 3075 2631 3366 Q 2631 3634 2415 3781 Q 2200 3928 1806 3928 Q 1516 3928 1219 3862 Q 922 3797 628 3669 L 628 4550 Q 984 4650 1334 4700 Q 1684 4750 2022 4750 Q 2931 4750 3382 4451 Q 3834 4153 3834 3553 Q 3834 3144 3618 2883 Q 3403 2622 2981 2516 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-2" onclick="(()=>{console.log('text 2 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(391.41758 187.661092) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-35"
|
||||
d="M 678 4666 L 3669 4666 L 3669 3781 L 1638 3781 L 1638 3059 Q 1775 3097 1914 3117 Q 2053 3138 2203 3138 Q 3056 3138 3531 2711 Q 4006 2284 4006 1522 Q 4006 766 3489 337 Q 2972 -91 2053 -91 Q 1656 -91 1267 -14 Q 878 63 494 219 L 494 1166 Q 875 947 1217 837 Q 1559 728 1863 728 Q 2300 728 2551 942 Q 2803 1156 2803 1522 Q 2803 1891 2551 2103 Q 2300 2316 1863 2316 Q 1603 2316 1309 2248 Q 1016 2181 678 2041 L 678 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-3" onclick="(()=>{console.log('text 3 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(162.43958 249.864438) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-36"
|
||||
d="M 2316 2303 Q 2000 2303 1842 2098 Q 1684 1894 1684 1484 Q 1684 1075 1842 870 Q 2000 666 2316 666 Q 2634 666 2792 870 Q 2950 1075 2950 1484 Q 2950 1894 2792 2098 Q 2634 2303 2316 2303 z M 3803 4544 L 3803 3681 Q 3506 3822 3243 3889 Q 2981 3956 2731 3956 Q 2194 3956 1894 3657 Q 1594 3359 1544 2772 Q 1750 2925 1990 3001 Q 2231 3078 2516 3078 Q 3231 3078 3670 2659 Q 4109 2241 4109 1563 Q 4109 813 3618 361 Q 3128 -91 2303 -91 Q 1394 -91 895 523 Q 397 1138 397 2266 Q 397 3422 980 4083 Q 1563 4744 2578 4744 Q 2900 4744 3203 4694 Q 3506 4644 3803 4544 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-4" onclick="(()=>{console.log('text 4 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.71135 398.663421) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-39"
|
||||
d="M 641 103 L 641 966 Q 928 831 1190 764 Q 1453 697 1709 697 Q 2247 697 2547 995 Q 2847 1294 2900 1881 Q 2688 1725 2447 1647 Q 2206 1569 1925 1569 Q 1209 1569 770 1986 Q 331 2403 331 3084 Q 331 3838 820 4291 Q 1309 4744 2131 4744 Q 3044 4744 3544 4128 Q 4044 3513 4044 2388 Q 4044 1231 3459 570 Q 2875 -91 1856 -91 Q 1528 -91 1228 -42 Q 928 6 641 103 z M 2125 2350 Q 2441 2350 2600 2554 Q 2759 2759 2759 3169 Q 2759 3575 2600 3781 Q 2441 3988 2125 3988 Q 1809 3988 1650 3781 Q 1491 3575 1491 3169 Q 1491 2759 1650 2554 Q 1809 2350 2125 2350 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-5" onclick="(()=>{console.log('text 5 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(283.349879 117.767448) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-6" onclick="(()=>{console.log('text 6 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(301.375385 350.124742) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-32"
|
||||
d="M 1844 884 L 3897 884 L 3897 0 L 506 0 L 506 884 L 2209 2388 Q 2438 2594 2547 2791 Q 2656 2988 2656 3200 Q 2656 3528 2436 3728 Q 2216 3928 1850 3928 Q 1569 3928 1234 3808 Q 900 3688 519 3450 L 519 4475 Q 925 4609 1322 4679 Q 1719 4750 2100 4750 Q 2938 4750 3402 4381 Q 3866 4013 3866 3353 Q 3866 2972 3669 2642 Q 3472 2313 2841 1759 L 1844 884 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-7" onclick="(()=>{console.log('text 7 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(388.046698 374.967662) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-8" onclick="(()=>{console.log('text 8 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(303.363625 192.113337) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-30"
|
||||
d="M 2944 2338 Q 2944 3213 2780 3570 Q 2616 3928 2228 3928 Q 1841 3928 1675 3570 Q 1509 3213 1509 2338 Q 1509 1453 1675 1090 Q 1841 728 2228 728 Q 2613 728 2778 1090 Q 2944 1453 2944 2338 z M 4147 2328 Q 4147 1169 3647 539 Q 3147 -91 2228 -91 Q 1306 -91 806 539 Q 306 1169 306 2328 Q 306 3491 806 4120 Q 1306 4750 2228 4750 Q 3147 4750 3647 4120 Q 4147 3491 4147 2328 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-9" onclick="(()=>{console.log('text 9 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(205.455661 289.031269) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-10" onclick="(()=>{console.log('text 10 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(318.115269 306.990997) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-11" onclick="(()=>{console.log('text 11 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(459.755142 293.508014) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-12" onclick="(()=>{console.log('text 12 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(161.065175 271.9803) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-38"
|
||||
d="M 2228 2088 Q 1891 2088 1709 1903 Q 1528 1719 1528 1375 Q 1528 1031 1709 848 Q 1891 666 2228 666 Q 2563 666 2741 848 Q 2919 1031 2919 1375 Q 2919 1722 2741 1905 Q 2563 2088 2228 2088 z M 1350 2484 Q 925 2613 709 2878 Q 494 3144 494 3541 Q 494 4131 934 4440 Q 1375 4750 2228 4750 Q 3075 4750 3515 4442 Q 3956 4134 3956 3541 Q 3956 3144 3739 2878 Q 3522 2613 3097 2484 Q 3572 2353 3814 2058 Q 4056 1763 4056 1313 Q 4056 619 3595 264 Q 3134 -91 2228 -91 Q 1319 -91 855 264 Q 391 619 391 1313 Q 391 1763 633 2058 Q 875 2353 1350 2484 z M 1631 3419 Q 1631 3141 1786 2991 Q 1941 2841 2228 2841 Q 2509 2841 2662 2991 Q 2816 3141 2816 3419 Q 2816 3697 2662 3845 Q 2509 3994 2228 3994 Q 1941 3994 1786 3844 Q 1631 3694 1631 3419 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-32" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-13" onclick="(()=>{console.log('text 13 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(244.173583 110.918523) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-14" onclick="(()=>{console.log('text 14 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(233.467625 161.664737) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-15" onclick="(()=>{console.log('text 15 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(270.491369 193.884188) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-16" onclick="(()=>{console.log('text 16 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(181.971765 226.625792) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-34"
|
||||
d="M 2356 3675 L 1038 1722 L 2356 1722 L 2356 3675 z M 2156 4666 L 3494 4666 L 3494 1722 L 4159 1722 L 4159 850 L 3494 850 L 3494 0 L 2356 0 L 2356 850 L 288 850 L 288 1881 L 2156 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-17" onclick="(()=>{console.log('text 17 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(225.063335 351.712921) scale(0.12 -0.12)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Bold-37"
|
||||
d="M 428 4666 L 3944 4666 L 3944 3988 L 2125 0 L 953 0 L 2675 3781 L 428 3781 L 428 4666 z "
|
||||
transform="scale(0.015625)" />
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Bold-33" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-18" onclick="(()=>{console.log('text 18 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(113.946108 355.813803) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-19" onclick="(()=>{console.log('text 19 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(150.275227 403.77299) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-20" onclick="(()=>{console.log('text 20 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(206.056148 247.886269) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-21" onclick="(()=>{console.log('text 21 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(256.219988 226.341346) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-22" onclick="(()=>{console.log('text 22 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(212.447115 398.180721) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-23" onclick="(()=>{console.log('text 23 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(335.953598 169.601466) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-24" onclick="(()=>{console.log('text 24 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(232.971874 258.553808) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-25" onclick="(()=>{console.log('text 25 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(248.771502 299.468132) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-34" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-26" onclick="(()=>{console.log('text 26 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(199.213878 206.324585) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-27" onclick="(()=>{console.log('text 27 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(444.964679 332.520349) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-32" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-28" onclick="(()=>{console.log('text 28 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(341.774186 339.623355) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-35" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-29" onclick="(()=>{console.log('text 29 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(278.831389 312.87527) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-36" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-30" onclick="(()=>{console.log('text 30 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(279.080561 152.246589) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-31" onclick="(()=>{console.log('text 31 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(273.529238 281.158194) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-32" onclick="(()=>{console.log('text 32 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(292.28304 437.124218) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-33" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-33" onclick="(()=>{console.log('text 33 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(207.847093 320.425118) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-34" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-34" onclick="(()=>{console.log('text 34 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(411.155112 337.45314) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-35" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-35" onclick="(()=>{console.log('text 35 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(312.721831 137.338276) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-36" onclick="(()=>{console.log('text 36 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.165623 477.463977) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-37" onclick="(()=>{console.log('text 37 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(200.10369 364.776639) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-38" onclick="(()=>{console.log('text 38 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(228.300336 209.562933) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-37" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-39" onclick="(()=>{console.log('text 39 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(290.459616 239.607741) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-40" onclick="(()=>{console.log('text 40 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(173.595109 322.147003) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-31" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-41" onclick="(()=>{console.log('text 41 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(358.784874 437.766204) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-36" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-42" onclick="(()=>{console.log('text 42 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(413.927594 407.153606) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-43" onclick="(()=>{console.log('text 43 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(312.741682 407.26454) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-44" onclick="(()=>{console.log('text 44 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(154.095894 358.795699) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-38" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-45" onclick="(()=>{console.log('text 45 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(332.723876 275.599102) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-37" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-46" onclick="(()=>{console.log('text 46 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(380.950141 419.578314) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-38" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-47" onclick="(()=>{console.log('text 47 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(205.582536 438.239079) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-39" />
|
||||
<use xlink:href="#DejaVuSans-Bold-39" x="69.580078" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="beatrice-text-male-48" onclick="(()=>{console.log('text 48 clicked')})()"
|
||||
class="beatrice-text-pointer">
|
||||
<g clip-path="url(#pd42c8a995e)">
|
||||
|
||||
<g transform="translate(423.537047 366.877303) scale(0.12 -0.12)">
|
||||
<use xlink:href="#DejaVuSans-Bold-31" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="69.580078" />
|
||||
<use xlink:href="#DejaVuSans-Bold-30" x="139.160156" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="pd42c8a995e">
|
||||
<rect x="85.985534" y="69.12" width="418.428931" height="443.52" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 54 KiB |
@ -21,8 +21,8 @@
|
||||
{
|
||||
"name": "configArea",
|
||||
"options": {
|
||||
"detectors": ["dio", "harvest", "crepe", "crepe_full", "crepe_tiny", "rmvpe", "rmvpe_onnx"],
|
||||
"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]
|
||||
"detectors": ["dio", "harvest", "crepe", "crepe_full", "crepe_tiny", "rmvpe", "rmvpe_onnx", "fcpe"],
|
||||
"inputChunkNums": [1, 2, 4, 6, 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, 4096, 8192, 16384]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
1
client/demo/public/assets/gui_settings/edition_web.txt
Normal file
1
client/demo/public/assets/gui_settings/edition_web.txt
Normal file
@ -0,0 +1 @@
|
||||
web
|
1
client/demo/public/info_web
Normal file
1
client/demo/public/info_web
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -13,6 +13,7 @@ import { AppRootProvider, useAppRoot } from "./001_provider/001_AppRootProvider"
|
||||
import { useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { Demo } from "./components/demo/010_Demo";
|
||||
import { useMessageBuilder } from "./hooks/useMessageBuilder";
|
||||
import { removeDB as webDBRemove } from "@dannadori/voice-changer-js";
|
||||
|
||||
library.add(fas, far, fab);
|
||||
|
||||
@ -57,6 +58,7 @@ const AppStateWrapper = () => {
|
||||
|
||||
const onClearCacheClicked = async () => {
|
||||
await removeDB();
|
||||
await webDBRemove();
|
||||
location.reload();
|
||||
};
|
||||
const onReloadClicked = () => {
|
||||
|
299
client/demo/src/001_globalHooks/100_useWebInfo.ts
Normal file
299
client/demo/src/001_globalHooks/100_useWebInfo.ts
Normal file
@ -0,0 +1,299 @@
|
||||
import { ClientState, WebModelSlot } from "@dannadori/voice-changer-client-js";
|
||||
import { VoiceChangerJSClientConfig, VoiceChangerJSClient, ProgressUpdateType, ProgreeeUpdateCallbcckInfo, VoiceChangerType, InputLengthKey, ResponseTimeInfo } from "@dannadori/voice-changer-js";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
export type UseWebInfoProps = {
|
||||
clientState: ClientState | null;
|
||||
webEdition: boolean;
|
||||
};
|
||||
|
||||
export const WebModelLoadingState = {
|
||||
none: "none",
|
||||
loading: "loading",
|
||||
warmup: "warmup",
|
||||
ready: "ready",
|
||||
} as const;
|
||||
export type WebModelLoadingState = (typeof WebModelLoadingState)[keyof typeof WebModelLoadingState];
|
||||
|
||||
export type VoiceChangerConfig = {
|
||||
config: VoiceChangerJSClientConfig;
|
||||
modelUrl: string;
|
||||
portrait: string;
|
||||
name: string;
|
||||
termOfUse: string;
|
||||
sampleRate: ModelSampleRateStr;
|
||||
useF0: boolean;
|
||||
inputLength: InputLengthKey;
|
||||
progressCallback?: ((data: any) => void) | null;
|
||||
};
|
||||
export type WebInfoState = {
|
||||
voiceChangerConfig: VoiceChangerConfig;
|
||||
webModelLoadingState: WebModelLoadingState;
|
||||
progressLoadPreprocess: number;
|
||||
progressLoadVCModel: number;
|
||||
progressWarmup: number;
|
||||
webModelslot: WebModelSlot;
|
||||
upkey: number;
|
||||
responseTimeInfo: ResponseTimeInfo;
|
||||
};
|
||||
export type WebInfoStateAndMethod = WebInfoState & {
|
||||
loadVoiceChanagerModel: () => Promise<void>;
|
||||
setUpkey: (upkey: number) => void;
|
||||
setVoiceChangerConfig: (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => void;
|
||||
};
|
||||
|
||||
const ModelSampleRateStr = {
|
||||
"40k": "40k",
|
||||
"32k": "32k",
|
||||
"16k": "16k",
|
||||
} as const;
|
||||
type ModelSampleRateStr = (typeof ModelSampleRateStr)[keyof typeof ModelSampleRateStr];
|
||||
|
||||
const noF0ModelUrl: { [modelType in VoiceChangerType]: { [inputLength in InputLengthKey]: { [sampleRate in ModelSampleRateStr]: string } } } = {
|
||||
rvcv1: {
|
||||
"24000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_24000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_24000.bin",
|
||||
},
|
||||
"16000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_16000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_16000.bin",
|
||||
},
|
||||
"12000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_12000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_12000.bin",
|
||||
},
|
||||
"8000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_8000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_8000.bin",
|
||||
},
|
||||
},
|
||||
rvcv2: {
|
||||
"24000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_24000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_24000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_24000.bin",
|
||||
},
|
||||
"16000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_16000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_16000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_16000.bin",
|
||||
},
|
||||
"12000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_12000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_12000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_12000.bin",
|
||||
},
|
||||
"8000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_8000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_8000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_8000.bin",
|
||||
},
|
||||
},
|
||||
};
|
||||
const f0ModelUrl: { [modelType in VoiceChangerType]: { [inputLength in InputLengthKey]: { [sampleRate in ModelSampleRateStr]: string } } } = {
|
||||
rvcv1: {
|
||||
"24000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_24000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_24000.bin",
|
||||
},
|
||||
"16000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_16000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_16000.bin",
|
||||
},
|
||||
"12000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_12000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_12000.bin",
|
||||
},
|
||||
"8000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_8000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_8000.bin",
|
||||
},
|
||||
},
|
||||
rvcv2: {
|
||||
"24000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_24000.bin",
|
||||
// "32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_24000.bin",
|
||||
"32k": "https://192.168.0.247:8080/models/rvcv2_exp_v2_32k_f0_24000.bin",
|
||||
// "16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_24000.bin",
|
||||
// "16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/vctk/rvcv2_vctk_v2_16k_f0_24000.bin",
|
||||
"16k": "https://192.168.0.247:8080/models/rvcv2_vctk_v2_16k_f0_24000.bin",
|
||||
},
|
||||
"16000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_16000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_16000.bin",
|
||||
// "16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_16000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/vctk/rvcv2_vctk_v2_16k_f0_16000.bin",
|
||||
},
|
||||
"12000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_12000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_12000.bin",
|
||||
// "16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_12000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/vctk/rvcv2_vctk_v2_16k_f0_16000.bin",
|
||||
},
|
||||
"8000": {
|
||||
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_8000.bin",
|
||||
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_8000.bin",
|
||||
// "16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_8000.bin",
|
||||
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/vctk/rvcv2_vctk_v2_16k_f0_8000.bin",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
|
||||
const initVoiceChangerType: VoiceChangerType = "rvcv2";
|
||||
const initInputLength: InputLengthKey = "24000";
|
||||
const initUseF0 = true;
|
||||
const initSampleRate: ModelSampleRateStr = "32k";
|
||||
|
||||
const progressCallback = (data: ProgreeeUpdateCallbcckInfo) => {
|
||||
if (data.progressUpdateType === ProgressUpdateType.loadPreprocessModel) {
|
||||
setProgressLoadPreprocess(data.progress);
|
||||
} else if (data.progressUpdateType === ProgressUpdateType.loadVCModel) {
|
||||
setProgressLoadVCModel(data.progress);
|
||||
} else if (data.progressUpdateType === ProgressUpdateType.checkResponseTime) {
|
||||
setProgressWarmup(data.progress);
|
||||
}
|
||||
};
|
||||
|
||||
const generateVoiceChangerConfig = (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => {
|
||||
let modelUrl;
|
||||
if (useF0) {
|
||||
modelUrl = f0ModelUrl[voiceChangerType][inputLength][sampleRate];
|
||||
} else {
|
||||
modelUrl = noF0ModelUrl[voiceChangerType][inputLength][sampleRate];
|
||||
}
|
||||
|
||||
const config: VoiceChangerConfig = {
|
||||
config: {
|
||||
voiceChangerType: voiceChangerType,
|
||||
inputLength: inputLength,
|
||||
baseUrl: window.location.origin,
|
||||
inputSamplingRate: 48000,
|
||||
outputSamplingRate: 48000,
|
||||
},
|
||||
modelUrl: modelUrl,
|
||||
portrait: "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/amitaro.png",
|
||||
name: "あみたろ",
|
||||
termOfUse: "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/amitaro_contentvec_256/term_of_use.txt",
|
||||
sampleRate: sampleRate,
|
||||
useF0: useF0,
|
||||
inputLength: inputLength,
|
||||
progressCallback,
|
||||
};
|
||||
return config;
|
||||
};
|
||||
|
||||
const [voiceChangerConfig, _setVoiceChangerConfig] = useState<VoiceChangerConfig>(generateVoiceChangerConfig(initVoiceChangerType, initSampleRate, initUseF0, initInputLength));
|
||||
const [webModelLoadingState, setWebModelLoadingState] = useState<WebModelLoadingState>(WebModelLoadingState.none);
|
||||
const [progressLoadPreprocess, setProgressLoadPreprocess] = useState<number>(0);
|
||||
const [progressLoadVCModel, setProgressLoadVCModel] = useState<number>(0);
|
||||
const [progressWarmup, setProgressWarmup] = useState<number>(0);
|
||||
const [upkey, setUpkey] = useState<number>(0);
|
||||
const [responseTimeInfo, setResponseTimeInfo] = useState<ResponseTimeInfo>({
|
||||
responseTime: 0,
|
||||
realDuration: 0,
|
||||
rtf: 0,
|
||||
});
|
||||
const voiceChangerJSClient = useRef<VoiceChangerJSClient>();
|
||||
|
||||
const webModelslot: WebModelSlot = useMemo(() => {
|
||||
return {
|
||||
slotIndex: -1,
|
||||
voiceChangerType: "WebModel",
|
||||
name: voiceChangerConfig.name,
|
||||
description: "",
|
||||
credit: "",
|
||||
termsOfUseUrl: voiceChangerConfig.termOfUse,
|
||||
iconFile: voiceChangerConfig.portrait,
|
||||
speakers: {},
|
||||
defaultTune: 0,
|
||||
modelType: "pyTorchRVCNono",
|
||||
f0: voiceChangerConfig.useF0,
|
||||
samplingRate: 0,
|
||||
modelFile: "",
|
||||
};
|
||||
}, []);
|
||||
|
||||
const setVoiceChangerConfig = (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => {
|
||||
const config = generateVoiceChangerConfig(voiceChangerType, sampleRate, useF0, inputLength);
|
||||
_setVoiceChangerConfig(config);
|
||||
};
|
||||
// useEffect(() => {
|
||||
// setVoiceChangerConfig({ ...voiceChangerConfig, progressCallback });
|
||||
// }, []);
|
||||
|
||||
const loadVoiceChanagerModel = async () => {
|
||||
if (!props.clientState) {
|
||||
throw new Error("[useWebInfo] clientState is null");
|
||||
}
|
||||
if (!props.clientState.initialized) {
|
||||
console.warn("[useWebInfo] clientState is not initialized yet");
|
||||
return;
|
||||
}
|
||||
if (!props.webEdition) {
|
||||
console.warn("[useWebInfo] this is not web edition");
|
||||
return;
|
||||
}
|
||||
console.log("loadVoiceChanagerModel1", voiceChangerConfig);
|
||||
setWebModelLoadingState("loading");
|
||||
voiceChangerJSClient.current = new VoiceChangerJSClient();
|
||||
await voiceChangerJSClient.current.initialize(voiceChangerConfig.config, voiceChangerConfig.modelUrl, voiceChangerConfig.progressCallback);
|
||||
console.log("loadVoiceChanagerModel2");
|
||||
|
||||
// worm up
|
||||
setWebModelLoadingState("warmup");
|
||||
const warmupResult = await voiceChangerJSClient.current.checkResponseTime();
|
||||
console.log("warmup result", warmupResult);
|
||||
|
||||
// check time
|
||||
const responseTimeInfo = await voiceChangerJSClient.current.checkResponseTime();
|
||||
console.log("responseTimeInfo", responseTimeInfo);
|
||||
setResponseTimeInfo(responseTimeInfo);
|
||||
|
||||
props.clientState?.setInternalAudioProcessCallback({
|
||||
processAudio: async (data: Uint8Array) => {
|
||||
const audioF32 = new Float32Array(data.buffer);
|
||||
const res = await voiceChangerJSClient.current!.convert(audioF32);
|
||||
const audio = new Uint8Array(res[0].buffer);
|
||||
if (res[1]) {
|
||||
console.log("RESPONSE!", res[1]);
|
||||
setResponseTimeInfo(res[1]);
|
||||
}
|
||||
return audio;
|
||||
},
|
||||
});
|
||||
setWebModelLoadingState("ready");
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!voiceChangerJSClient.current) {
|
||||
console.log("setupkey", voiceChangerJSClient.current);
|
||||
return;
|
||||
}
|
||||
voiceChangerJSClient.current.setUpkey(upkey);
|
||||
}, [upkey]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("change voice ", voiceChangerConfig);
|
||||
|
||||
setProgressLoadPreprocess(0);
|
||||
setProgressLoadVCModel(0);
|
||||
setProgressWarmup(0);
|
||||
|
||||
loadVoiceChanagerModel();
|
||||
}, [voiceChangerConfig, props.clientState?.initialized]);
|
||||
|
||||
return {
|
||||
voiceChangerConfig,
|
||||
webModelLoadingState,
|
||||
progressLoadPreprocess,
|
||||
progressLoadVCModel,
|
||||
progressWarmup,
|
||||
webModelslot,
|
||||
upkey,
|
||||
responseTimeInfo,
|
||||
loadVoiceChanagerModel,
|
||||
setUpkey,
|
||||
setVoiceChangerConfig,
|
||||
};
|
||||
};
|
@ -8,10 +8,10 @@ type Props = {
|
||||
};
|
||||
|
||||
type AppRootValue = {
|
||||
audioContextState: AudioConfigState
|
||||
appGuiSettingState: AppGuiSettingStateAndMethod
|
||||
getGUISetting: () => Promise<void>
|
||||
}
|
||||
audioContextState: AudioConfigState;
|
||||
appGuiSettingState: AppGuiSettingStateAndMethod;
|
||||
getGUISetting: () => Promise<void>;
|
||||
};
|
||||
|
||||
const AppRootContext = React.createContext<AppRootValue | null>(null);
|
||||
export const useAppRoot = (): AppRootValue => {
|
||||
@ -23,17 +23,16 @@ export const useAppRoot = (): AppRootValue => {
|
||||
};
|
||||
|
||||
export const AppRootProvider = ({ children }: Props) => {
|
||||
const audioContextState = useAudioConfig()
|
||||
const appGuiSettingState = useAppGuiSetting()
|
||||
|
||||
const audioContextState = useAudioConfig();
|
||||
const appGuiSettingState = useAppGuiSetting();
|
||||
|
||||
const getGUISetting = async () => {
|
||||
await appGuiSettingState.getAppGuiSetting(`/assets/gui_settings/GUI.json`)
|
||||
}
|
||||
await appGuiSettingState.getAppGuiSetting(`/assets/gui_settings/GUI.json`);
|
||||
};
|
||||
const providerValue: AppRootValue = {
|
||||
audioContextState,
|
||||
appGuiSettingState,
|
||||
getGUISetting
|
||||
getGUISetting,
|
||||
};
|
||||
return <AppRootContext.Provider value={providerValue}>{children}</AppRootContext.Provider>;
|
||||
};
|
||||
|
@ -1,18 +1,22 @@
|
||||
import { ClientState } from "@dannadori/voice-changer-client-js";
|
||||
import { VoiceChangerJSClient } from "@dannadori/voice-changer-js";
|
||||
import React, { useContext, useEffect, useRef } from "react";
|
||||
import { ReactNode } from "react";
|
||||
import { useVCClient } from "../001_globalHooks/001_useVCClient";
|
||||
import { useAppRoot } from "./001_AppRootProvider";
|
||||
import { useMessageBuilder } from "../hooks/useMessageBuilder";
|
||||
import { WebInfoStateAndMethod, useWebInfo } from "../001_globalHooks/100_useWebInfo";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type AppStateValue = ClientState & {
|
||||
audioContext: AudioContext
|
||||
initializedRef: React.MutableRefObject<boolean>
|
||||
}
|
||||
audioContext: AudioContext;
|
||||
initializedRef: React.MutableRefObject<boolean>;
|
||||
webInfoState: WebInfoStateAndMethod;
|
||||
webEdition: boolean;
|
||||
};
|
||||
|
||||
const AppStateContext = React.createContext<AppStateValue | null>(null);
|
||||
export const useAppState = (): AppStateValue => {
|
||||
@ -24,41 +28,51 @@ export const useAppState = (): AppStateValue => {
|
||||
};
|
||||
|
||||
export const AppStateProvider = ({ children }: Props) => {
|
||||
const appRoot = useAppRoot()
|
||||
const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext })
|
||||
const messageBuilderState = useMessageBuilder()
|
||||
const appRoot = useAppRoot();
|
||||
const webEdition = appRoot.appGuiSettingState.edition.indexOf("web") >= 0;
|
||||
const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext });
|
||||
const messageBuilderState = useMessageBuilder();
|
||||
const webInfoState = useWebInfo({ clientState: clientState.clientState, webEdition: webEdition });
|
||||
// const voiceChangerJSClient = useRef<VoiceChangerJSClient>();
|
||||
|
||||
useEffect(() => {
|
||||
messageBuilderState.setMessage(__filename, "ioError", {
|
||||
"ja": "エラーが頻発しています。対象としているフレームワークのモデルがロードされているか確認してください。",
|
||||
"en": "Frequent errors occur. Please check if the model of the framework being targeted is loaded."
|
||||
})
|
||||
}, [])
|
||||
ja: "エラーが頻発しています。対象としているフレームワークのモデルがロードされているか確認してください。",
|
||||
en: "Frequent errors occur. Please check if the model of the framework being targeted is loaded.",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const initializedRef = useRef<boolean>(false)
|
||||
const initializedRef = useRef<boolean>(false);
|
||||
useEffect(() => {
|
||||
if (clientState.clientState.initialized) {
|
||||
initializedRef.current = true
|
||||
clientState.clientState.getInfo()
|
||||
initializedRef.current = true;
|
||||
clientState.clientState.getInfo();
|
||||
// clientState.clientState.setVoiceChangerClientSetting({
|
||||
// ...clientState.clientState.setting.voiceChangerClientSetting
|
||||
// })
|
||||
}
|
||||
}, [clientState.clientState.initialized])
|
||||
}, [clientState.clientState.initialized]);
|
||||
|
||||
useEffect(() => {
|
||||
if (clientState.clientState.ioErrorCount > 100) {
|
||||
alert(messageBuilderState.getMessage(__filename, "ioError"))
|
||||
clientState.clientState.resetIoErrorCount()
|
||||
alert(messageBuilderState.getMessage(__filename, "ioError"));
|
||||
clientState.clientState.resetIoErrorCount();
|
||||
}
|
||||
}, [clientState.clientState.ioErrorCount]);
|
||||
|
||||
}, [clientState.clientState.ioErrorCount])
|
||||
|
||||
useEffect(() => {
|
||||
if (appRoot.appGuiSettingState.edition.indexOf("web") >= 0 && clientState.clientState.initialized) {
|
||||
clientState.clientState.setWorkletNodeSetting({ ...clientState.clientState.setting.workletNodeSetting, protocol: "internal" });
|
||||
// webInfoState.loadVoiceChanagerModel(); // hook内でuseEffectでinvoke
|
||||
}
|
||||
}, [clientState.clientState.initialized]);
|
||||
|
||||
const providerValue: AppStateValue = {
|
||||
audioContext: appRoot.audioContextState.audioContext!,
|
||||
...clientState.clientState,
|
||||
initializedRef,
|
||||
webInfoState,
|
||||
webEdition,
|
||||
};
|
||||
|
||||
return <AppStateContext.Provider value={providerValue}>{children}</AppStateContext.Provider>;
|
||||
|
41
client/demo/src/001_provider/_BlockingQueue.ts
Normal file
41
client/demo/src/001_provider/_BlockingQueue.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export class BlockingQueue<T> {
|
||||
private _promises: Promise<T>[];
|
||||
private _resolvers: ((t: T) => void)[];
|
||||
|
||||
constructor() {
|
||||
this._resolvers = [];
|
||||
this._promises = [];
|
||||
}
|
||||
|
||||
private _add() {
|
||||
this._promises.push(
|
||||
new Promise((resolve) => {
|
||||
this._resolvers.push(resolve);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
enqueue(t: T) {
|
||||
if (this._resolvers.length == 0) this._add();
|
||||
const resolve = this._resolvers.shift()!;
|
||||
resolve(t);
|
||||
}
|
||||
|
||||
dequeue() {
|
||||
if (this._promises.length == 0) this._add();
|
||||
const promise = this._promises.shift()!;
|
||||
return promise;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this._promises.length == 0;
|
||||
}
|
||||
|
||||
isBlocked() {
|
||||
return this._resolvers.length != 0;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._promises.length - this._resolvers.length;
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import React, { useContext, useEffect, useState, useRef } from "react";
|
||||
import { ReactNode } from "react";
|
||||
import { useAppRoot } from "../../001_provider/001_AppRootProvider";
|
||||
import { StateControlCheckbox, useStateControlCheckbox } from "../../hooks/useStateControlCheckbox";
|
||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||
|
||||
export const OpenServerControlCheckbox = "open-server-control-checkbox";
|
||||
export const OpenModelSettingCheckbox = "open-model-setting-checkbox";
|
||||
@ -61,6 +62,7 @@ type GuiStateAndMethod = {
|
||||
setIsAnalyzing: (val: boolean) => void;
|
||||
setShowPyTorchModelUpload: (val: boolean) => void;
|
||||
|
||||
reloadDeviceInfo: () => Promise<void>;
|
||||
inputAudioDeviceInfo: MediaDeviceInfo[];
|
||||
outputAudioDeviceInfo: MediaDeviceInfo[];
|
||||
audioInputForGUI: string;
|
||||
@ -83,6 +85,12 @@ type GuiStateAndMethod = {
|
||||
|
||||
textInputResolve: TextInputResolveType | null;
|
||||
setTextInputResolve: (val: TextInputResolveType | null) => void;
|
||||
|
||||
// for Beatrice
|
||||
beatriceJVSSpeakerId: number;
|
||||
beatriceJVSSpeakerPitch: number;
|
||||
setBeatriceJVSSpeakerId: (id: number) => void;
|
||||
setBeatriceJVSSpeakerPitch: (pitch: number) => void;
|
||||
};
|
||||
|
||||
const GuiStateContext = React.createContext<GuiStateAndMethod | null>(null);
|
||||
@ -100,6 +108,7 @@ type TextInputResolveType = {
|
||||
|
||||
export const GuiStateProvider = ({ children }: Props) => {
|
||||
const { appGuiSettingState } = useAppRoot();
|
||||
const { serverSetting } = useAppState();
|
||||
const [isConverting, setIsConverting] = useState<boolean>(false);
|
||||
const [isAnalyzing, setIsAnalyzing] = useState<boolean>(false);
|
||||
const [modelSlotNum, setModelSlotNum] = useState<number>(0);
|
||||
@ -117,14 +126,23 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
|
||||
const [textInputResolve, setTextInputResolve] = useState<TextInputResolveType | null>(null);
|
||||
|
||||
const reloadDeviceInfo = async () => {
|
||||
try {
|
||||
const ms = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
|
||||
ms.getTracks().forEach((x) => {
|
||||
x.stop();
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn("Enumerate device error::", e);
|
||||
const [beatriceJVSSpeakerId, setBeatriceJVSSpeakerId] = useState<number>(1);
|
||||
const [beatriceJVSSpeakerPitch, setBeatriceJVSSpeakerPitch] = useState<number>(0);
|
||||
|
||||
const checkDeviceAvailable = useRef<boolean>(false);
|
||||
|
||||
const _reloadDeviceInfo = async () => {
|
||||
// デバイスチェックの空振り
|
||||
if (checkDeviceAvailable.current == false) {
|
||||
try {
|
||||
const ms = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
|
||||
ms.getTracks().forEach((x) => {
|
||||
x.stop();
|
||||
});
|
||||
checkDeviceAvailable.current = true;
|
||||
} catch (e) {
|
||||
console.warn("Enumerate device error::", e);
|
||||
}
|
||||
}
|
||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
|
||||
@ -171,14 +189,66 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
// })
|
||||
return [audioInputs, audioOutputs];
|
||||
};
|
||||
|
||||
const reloadDeviceInfo = async () => {
|
||||
const audioInfo = await _reloadDeviceInfo();
|
||||
setInputAudioDeviceInfo(audioInfo[0]);
|
||||
setOutputAudioDeviceInfo(audioInfo[1]);
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// const audioInitialize = async () => {
|
||||
// await reloadDeviceInfo();
|
||||
// };
|
||||
// audioInitialize();
|
||||
// }, []);
|
||||
|
||||
useEffect(() => {
|
||||
const audioInitialize = async () => {
|
||||
const audioInfo = await reloadDeviceInfo();
|
||||
setInputAudioDeviceInfo(audioInfo[0]);
|
||||
setOutputAudioDeviceInfo(audioInfo[1]);
|
||||
let isMounted = true;
|
||||
|
||||
// デバイスのポーリングを再帰的に実行する関数
|
||||
const pollDevices = async () => {
|
||||
const checkDeviceDiff = (knownDeviceIds: Set<string>, newDeviceIds: Set<string>) => {
|
||||
const deleted = new Set([...knownDeviceIds].filter((x) => !newDeviceIds.has(x)));
|
||||
const added = new Set([...newDeviceIds].filter((x) => !knownDeviceIds.has(x)));
|
||||
return { deleted, added };
|
||||
};
|
||||
try {
|
||||
const audioInfo = await _reloadDeviceInfo();
|
||||
|
||||
const knownAudioinputIds = new Set(inputAudioDeviceInfo.map((x) => x.deviceId));
|
||||
const newAudioinputIds = new Set(audioInfo[0].map((x) => x.deviceId));
|
||||
|
||||
const knownAudiooutputIds = new Set(outputAudioDeviceInfo.map((x) => x.deviceId));
|
||||
const newAudiooutputIds = new Set(audioInfo[1].map((x) => x.deviceId));
|
||||
|
||||
const audioInputDiff = checkDeviceDiff(knownAudioinputIds, newAudioinputIds);
|
||||
const audioOutputDiff = checkDeviceDiff(knownAudiooutputIds, newAudiooutputIds);
|
||||
|
||||
if (audioInputDiff.deleted.size > 0 || audioInputDiff.added.size > 0) {
|
||||
console.log(`deleted input device: ${[...audioInputDiff.deleted]}`);
|
||||
console.log(`added input device: ${[...audioInputDiff.added]}`);
|
||||
setInputAudioDeviceInfo(audioInfo[0]);
|
||||
}
|
||||
if (audioOutputDiff.deleted.size > 0 || audioOutputDiff.added.size > 0) {
|
||||
console.log(`deleted output device: ${[...audioOutputDiff.deleted]}`);
|
||||
console.log(`added output device: ${[...audioOutputDiff.added]}`);
|
||||
setOutputAudioDeviceInfo(audioInfo[1]);
|
||||
}
|
||||
|
||||
if (isMounted) {
|
||||
setTimeout(pollDevices, 1000 * 3);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("An error occurred during enumeration of devices:", err);
|
||||
}
|
||||
};
|
||||
audioInitialize();
|
||||
}, []);
|
||||
|
||||
pollDevices();
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [inputAudioDeviceInfo, outputAudioDeviceInfo]);
|
||||
|
||||
// (1) Controller Switch
|
||||
const openServerControlCheckbox = useStateControlCheckbox(OpenServerControlCheckbox);
|
||||
@ -242,7 +312,25 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
setTimeout(show);
|
||||
}, [appGuiSettingState.edition]);
|
||||
|
||||
const providerValue = {
|
||||
useEffect(() => {
|
||||
let dstId;
|
||||
if (beatriceJVSSpeakerPitch == 0) {
|
||||
dstId = (beatriceJVSSpeakerId - 1) * 5;
|
||||
} else if (beatriceJVSSpeakerPitch == 1) {
|
||||
dstId = (beatriceJVSSpeakerId - 1) * 5 + 1;
|
||||
} else if (beatriceJVSSpeakerPitch == 2) {
|
||||
dstId = (beatriceJVSSpeakerId - 1) * 5 + 2;
|
||||
} else if (beatriceJVSSpeakerPitch == -1) {
|
||||
dstId = (beatriceJVSSpeakerId - 1) * 5 + 3;
|
||||
} else if (beatriceJVSSpeakerPitch == -2) {
|
||||
dstId = (beatriceJVSSpeakerId - 1) * 5 + 4;
|
||||
} else {
|
||||
throw new Error(`invalid beatriceJVSSpeakerPitch speaker:${beatriceJVSSpeakerId} pitch:${beatriceJVSSpeakerPitch}`);
|
||||
}
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, dstId: dstId });
|
||||
}, [beatriceJVSSpeakerId, beatriceJVSSpeakerPitch]);
|
||||
|
||||
const providerValue: GuiStateAndMethod = {
|
||||
stateControls: {
|
||||
openServerControlCheckbox,
|
||||
openModelSettingCheckbox,
|
||||
@ -296,6 +384,12 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
|
||||
textInputResolve,
|
||||
setTextInputResolve,
|
||||
|
||||
// For Beatrice
|
||||
beatriceJVSSpeakerId,
|
||||
beatriceJVSSpeakerPitch,
|
||||
setBeatriceJVSSpeakerId,
|
||||
setBeatriceJVSSpeakerPitch,
|
||||
};
|
||||
return <GuiStateContext.Provider value={providerValue}>{children}</GuiStateContext.Provider>;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from "react"
|
||||
import React from "react";
|
||||
import { GuiStateProvider } from "./001_GuiStateProvider";
|
||||
import { Dialogs } from "./900_Dialogs";
|
||||
import { ModelSlotControl } from "./b00_ModelSlotControl";
|
||||
@ -13,5 +13,5 @@ export const Demo = () => {
|
||||
<ModelSlotControl></ModelSlotControl>
|
||||
</div>
|
||||
</GuiStateProvider>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -19,6 +19,17 @@ export const StartingNoticeDialog = () => {
|
||||
ja: "(1) 一部の設定変更を行うとgpuを使用していても変換処理が遅くなることが発生します。もしこの現象が発生したらGPUの値を-1にしてから再度0に戻してください。",
|
||||
en: "(1) When some settings are changed, conversion process becomes slow even when using GPU. If this occurs, reset the GPU value to -1 and then back to 0.",
|
||||
});
|
||||
messageBuilderState.setMessage(__filename, "web_edditon_1", { ja: "このWebエディションは実験的バージョンです。", en: "This edition(web) is an experimental Edition." });
|
||||
messageBuilderState.setMessage(__filename, "web_edditon_2", {
|
||||
ja: "より高機能・高性能なFullエディションは、",
|
||||
en: "The more advanced and high-performance Full Edition can be obtained for free from the following GitHub repository.",
|
||||
});
|
||||
messageBuilderState.setMessage(__filename, "web_edditon_3", {
|
||||
ja: "次のgithubリポジトリから無料で取得できます。",
|
||||
en: "",
|
||||
});
|
||||
messageBuilderState.setMessage(__filename, "github", { ja: "github", en: "github" });
|
||||
|
||||
messageBuilderState.setMessage(__filename, "click_to_start", { ja: "スタートボタンを押してください。", en: "Click to start" });
|
||||
messageBuilderState.setMessage(__filename, "start", { ja: "スタート", en: "start" });
|
||||
}, []);
|
||||
@ -44,7 +55,6 @@ export const StartingNoticeDialog = () => {
|
||||
|
||||
const licenseNoticeLink = useMemo(() => {
|
||||
return isDesktopApp() ? (
|
||||
// @ts-ignore
|
||||
<span
|
||||
className="link"
|
||||
onClick={() => {
|
||||
@ -95,6 +105,34 @@ export const StartingNoticeDialog = () => {
|
||||
|
||||
const licenseInfo = <div className="dialog-content-part">{licenseNoticeLink}</div>;
|
||||
|
||||
const webEdtionMessage = (
|
||||
<div className="dialog-content-part">
|
||||
<div>{messageBuilderState.getMessage(__filename, "web_edditon_1")}</div>
|
||||
<div>{messageBuilderState.getMessage(__filename, "web_edditon_2")}</div>
|
||||
<div>{messageBuilderState.getMessage(__filename, "web_edditon_3")}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const githubLink = isDesktopApp() ? (
|
||||
<span
|
||||
className="link tooltip"
|
||||
onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer");
|
||||
}}
|
||||
>
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
<div>github</div>
|
||||
</span>
|
||||
) : (
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<span>github</span>
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
</a>
|
||||
);
|
||||
|
||||
const clickToStartMessage = (
|
||||
<div className="dialog-content-part">
|
||||
<div>{messageBuilderState.getMessage(__filename, "click_to_start")}</div>
|
||||
@ -110,12 +148,19 @@ export const StartingNoticeDialog = () => {
|
||||
{clickToStartMessage}
|
||||
</div>
|
||||
);
|
||||
const contentForWeb = (
|
||||
<div className="body-row">
|
||||
{webEdtionMessage}
|
||||
{githubLink}
|
||||
{clickToStartMessage}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="dialog-frame">
|
||||
<div className="dialog-title">Message</div>
|
||||
<div className="dialog-content">
|
||||
{content}
|
||||
{edition.indexOf("web") >= 0 ? contentForWeb : content}
|
||||
{closeButtonRow}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,6 +116,20 @@ export const FileUploaderScreen = (props: FileUploaderScreenProps) => {
|
||||
return x.kind == "beatriceModel";
|
||||
});
|
||||
return enough;
|
||||
} else if (setting.voiceChangerType == "LLVC") {
|
||||
const enough =
|
||||
!!setting.files.find((x) => {
|
||||
return x.kind == "llvcModel";
|
||||
}) &&
|
||||
!!setting.files.find((x) => {
|
||||
return x.kind == "llvcConfig";
|
||||
});
|
||||
return enough;
|
||||
} else if (setting.voiceChangerType == "EasyVC") {
|
||||
const enough = !!setting.files.find((x) => {
|
||||
return x.kind == "easyVCModel";
|
||||
});
|
||||
return enough;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@ -177,6 +191,11 @@ export const FileUploaderScreen = (props: FileUploaderScreenProps) => {
|
||||
rows.push(generateFileRow(uploadSetting!, "Model(combo)", "diffusionSVCModel", ["ptc"]));
|
||||
} else if (vcType == "Beatrice") {
|
||||
rows.push(generateFileRow(uploadSetting!, "Beatrice", "beatriceModel", ["bin"]));
|
||||
} else if (vcType == "LLVC") {
|
||||
rows.push(generateFileRow(uploadSetting!, "Model", "llvcModel", ["pth"]));
|
||||
rows.push(generateFileRow(uploadSetting!, "Config", "llvcConfig", ["json"]));
|
||||
} else if (vcType == "EasyVC") {
|
||||
rows.push(generateFileRow(uploadSetting!, "Model", "easyVCModel", ["onnx"]));
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
|
@ -5,122 +5,123 @@ import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { useMessageBuilder } from "../../../hooks/useMessageBuilder";
|
||||
|
||||
|
||||
export type HeaderAreaProps = {
|
||||
mainTitle: string
|
||||
subTitle: string
|
||||
}
|
||||
mainTitle: string;
|
||||
subTitle: string;
|
||||
};
|
||||
|
||||
export const HeaderArea = (props: HeaderAreaProps) => {
|
||||
const { appGuiSettingState } = useAppRoot()
|
||||
const messageBuilderState = useMessageBuilder()
|
||||
const { clearSetting } = useAppState()
|
||||
const { appGuiSettingState } = useAppRoot();
|
||||
const messageBuilderState = useMessageBuilder();
|
||||
const { clearSetting, webInfoState } = useAppState();
|
||||
|
||||
const { removeItem } = useIndexedDB({ clientType: null })
|
||||
const { removeItem, removeDB } = useIndexedDB({ clientType: null });
|
||||
|
||||
useMemo(() => {
|
||||
messageBuilderState.setMessage(__filename, "github", { "ja": "github", "en": "github" })
|
||||
messageBuilderState.setMessage(__filename, "manual", { "ja": "マニュアル", "en": "manual" })
|
||||
messageBuilderState.setMessage(__filename, "screenCapture", { "ja": "録画ツール", "en": "Record Screen" })
|
||||
messageBuilderState.setMessage(__filename, "support", { "ja": "支援", "en": "Donation" })
|
||||
}, [])
|
||||
|
||||
messageBuilderState.setMessage(__filename, "github", { ja: "github", en: "github" });
|
||||
messageBuilderState.setMessage(__filename, "manual", { ja: "マニュアル", en: "manual" });
|
||||
messageBuilderState.setMessage(__filename, "screenCapture", { ja: "録画ツール", en: "Record Screen" });
|
||||
messageBuilderState.setMessage(__filename, "support", { ja: "支援", en: "Donation" });
|
||||
}, []);
|
||||
|
||||
const githubLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer") }}>
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
|
||||
return isDesktopApp() ? (
|
||||
<span
|
||||
className="link tooltip"
|
||||
onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer");
|
||||
}}
|
||||
>
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
</span>
|
||||
) : (
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">{messageBuilderState.getMessage(__filename, "github")}</div>
|
||||
</a>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const manualLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md") }}>
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "manual")}</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "manual")}</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
|
||||
return isDesktopApp() ? (
|
||||
<span
|
||||
className="link tooltip"
|
||||
onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md");
|
||||
}}
|
||||
>
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "manual")}</div>
|
||||
</span>
|
||||
) : (
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "manual")}</div>
|
||||
</a>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const toolLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p onClick={() => {
|
||||
return isDesktopApp() ? (
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p
|
||||
onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://w-okada.github.io/screen-recorder-ts/")
|
||||
}}>
|
||||
{messageBuilderState.getMessage(__filename, "screenCapture")}
|
||||
</p>
|
||||
</div>
|
||||
window.electronAPI.openBrowser("https://w-okada.github.io/screen-recorder-ts/");
|
||||
}}
|
||||
>
|
||||
{messageBuilderState.getMessage(__filename, "screenCapture")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
:
|
||||
(
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p onClick={() => {
|
||||
window.open("https://w-okada.github.io/screen-recorder-ts/", '_blank', "noreferrer")
|
||||
}}>
|
||||
{messageBuilderState.getMessage(__filename, "screenCapture")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p
|
||||
onClick={() => {
|
||||
window.open("https://w-okada.github.io/screen-recorder-ts/", "_blank", "noreferrer");
|
||||
}}
|
||||
>
|
||||
{messageBuilderState.getMessage(__filename, "screenCapture")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}, [])
|
||||
|
||||
</div>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const coffeeLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://www.buymeacoffee.com/wokad") }}>
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "support")}</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://www.buymeacoffee.com/wokad" target="_blank" rel="noopener noreferrer">
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
{messageBuilderState.getMessage(__filename, "support")}
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
return isDesktopApp() ? (
|
||||
<span
|
||||
className="link tooltip"
|
||||
onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://www.buymeacoffee.com/wokad");
|
||||
}}
|
||||
>
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "support")}</div>
|
||||
</span>
|
||||
) : (
|
||||
<a className="link tooltip" href="https://www.buymeacoffee.com/wokad" target="_blank" rel="noopener noreferrer">
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">{messageBuilderState.getMessage(__filename, "support")}</div>
|
||||
</a>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const headerArea = useMemo(() => {
|
||||
const onClearSettingClicked = async () => {
|
||||
await clearSetting()
|
||||
await removeItem(INDEXEDDB_KEY_AUDIO_OUTPUT)
|
||||
location.reload()
|
||||
}
|
||||
await clearSetting();
|
||||
await removeItem(INDEXEDDB_KEY_AUDIO_OUTPUT);
|
||||
await removeDB();
|
||||
location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="headerArea">
|
||||
@ -139,15 +140,16 @@ export const HeaderArea = (props: HeaderAreaProps) => {
|
||||
{/* {licenseButton} */}
|
||||
</span>
|
||||
<span className="belongings">
|
||||
<div className="belongings-button" onClick={onClearSettingClicked}>clear setting</div>
|
||||
<div className="belongings-button" onClick={onClearSettingClicked}>
|
||||
clear setting
|
||||
</div>
|
||||
{/* <div className="belongings-button" onClick={onReloadClicked}>reload</div>
|
||||
<div className="belongings-button" onClick={onReselectVCClicked}>select vc</div> */}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}, [props.subTitle, props.mainTitle, appGuiSettingState.version, appGuiSettingState.edition])
|
||||
);
|
||||
}, [props.subTitle, props.mainTitle, appGuiSettingState.version, appGuiSettingState.edition]);
|
||||
|
||||
return headerArea
|
||||
return headerArea;
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ const SortTypes = {
|
||||
export type SortTypes = (typeof SortTypes)[keyof typeof SortTypes];
|
||||
|
||||
export const ModelSlotArea = (_props: ModelSlotAreaProps) => {
|
||||
const { serverSetting, getInfo } = useAppState();
|
||||
const { serverSetting, getInfo, webEdition } = useAppState();
|
||||
const guiState = useGuiState();
|
||||
const messageBuilderState = useMessageBuilder();
|
||||
const [sortType, setSortType] = useState<SortTypes>("slot");
|
||||
@ -41,10 +41,14 @@ export const ModelSlotArea = (_props: ModelSlotAreaProps) => {
|
||||
const tileContainerClass = x.slotIndex == 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 modelDir = x.slotIndex == "Beatrice-JVS" ? "model_dir_static" : serverSetting.serverSetting.voiceChangerParams.model_dir;
|
||||
const icon = x.iconFile.length > 0 ? modelDir + "/" + x.slotIndex + "/" + x.iconFile.split(/[\/\\]/).pop() : "./assets/icons/human.png";
|
||||
|
||||
const iconElem =
|
||||
x.iconFile.length > 0 ? (
|
||||
<>
|
||||
<img className="model-slot-tile-icon" src={serverSetting.serverSetting.voiceChangerParams.model_dir + "/" + x.slotIndex + "/" + x.iconFile.split(/[\/\\]/).pop()} alt={x.name} />
|
||||
{/* <img className="model-slot-tile-icon" src={serverSetting.serverSetting.voiceChangerParams.model_dir + "/" + x.slotIndex + "/" + x.iconFile.split(/[\/\\]/).pop()} alt={x.name} /> */}
|
||||
<img className="model-slot-tile-icon" src={icon} alt={x.name} />
|
||||
<div className="model-slot-tile-vctype">{x.voiceChangerType}</div>
|
||||
</>
|
||||
) : (
|
||||
@ -55,6 +59,7 @@ export const ModelSlotArea = (_props: ModelSlotAreaProps) => {
|
||||
);
|
||||
|
||||
const clickAction = async () => {
|
||||
// @ts-ignore
|
||||
const dummyModelSlotIndex = Math.floor(Date.now() / 1000) * 1000 + x.slotIndex;
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, modelSlotIndex: dummyModelSlotIndex });
|
||||
setTimeout(() => {
|
||||
@ -111,5 +116,9 @@ export const ModelSlotArea = (_props: ModelSlotAreaProps) => {
|
||||
);
|
||||
}, [modelTiles, sortType]);
|
||||
|
||||
if (webEdition) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return modelSlotArea;
|
||||
};
|
||||
|
211
client/demo/src/components/demo/components2/101-0_Portrait.tsx
Normal file
211
client/demo/src/components/demo/components2/101-0_Portrait.tsx
Normal file
@ -0,0 +1,211 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useMessageBuilder } from "../../../hooks/useMessageBuilder";
|
||||
export type PortraitProps = {};
|
||||
const BeatriceSpeakerType = {
|
||||
male: "male",
|
||||
female: "female",
|
||||
} as const;
|
||||
type BeatriceSpeakerType = (typeof BeatriceSpeakerType)[keyof typeof BeatriceSpeakerType];
|
||||
|
||||
// @ts-ignore
|
||||
import MyIcon from "./female-clickable.svg";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
export const Portrait = (_props: PortraitProps) => {
|
||||
const { serverSetting, volume, bufferingTime, performance, webInfoState, webEdition } = useAppState();
|
||||
const messageBuilderState = useMessageBuilder();
|
||||
const [beatriceSpeakerType, setBeatriceSpeakerType] = useState<BeatriceSpeakerType>(BeatriceSpeakerType.male);
|
||||
const [beatriceSpeakerIndexInGender, setBeatriceSpeakerIndexInGender] = useState<string>("");
|
||||
const { setBeatriceJVSSpeakerId } = useGuiState();
|
||||
|
||||
const beatriceMaleSpeakersList = [1, 3, 5, 6, 9, 11, 12, 13, 20, 21, 22, 23, 28, 31, 32, 33, 34, 37, 41, 42, 44, 45, 46, 47, 48, 49, 50, 52, 54, 68, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 86, 87, 88, 89, 97, 98, 99, 100];
|
||||
const beatriceFemaleSpeakersList = [2, 4, 7, 8, 10, 14, 15, 16, 17, 18, 19, 24, 25, 26, 27, 29, 30, 35, 36, 38, 39, 40, 43, 51, 53, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 72, 82, 83, 84, 85, 90, 91, 92, 93, 94, 95, 96];
|
||||
|
||||
useMemo(() => {
|
||||
messageBuilderState.setMessage(__filename, "terms_of_use", { ja: "利用規約", en: "terms of use" });
|
||||
}, []);
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (webEdition) {
|
||||
return webInfoState.webModelslot;
|
||||
}
|
||||
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots, webEdition]);
|
||||
|
||||
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;
|
||||
const rtf = document.getElementById("status-rtf") as HTMLSpanElement;
|
||||
if (!vol || !buf || !res) {
|
||||
return;
|
||||
}
|
||||
vol.innerText = volume.toFixed(4);
|
||||
if (webEdition) {
|
||||
buf.innerText = bufferingTime.toString();
|
||||
res.innerText = webInfoState.responseTimeInfo.responseTime.toString() ?? "0";
|
||||
rtf.innerText = webInfoState.responseTimeInfo.rtf.toString() ?? "0";
|
||||
} else {
|
||||
buf.innerText = bufferingTime.toString();
|
||||
res.innerText = performance.responseTime.toString();
|
||||
}
|
||||
}, [volume, bufferingTime, performance, webInfoState.responseTimeInfo]);
|
||||
|
||||
const setSelectedClass = () => {
|
||||
const iframe = document.querySelector(".beatrice-speaker-graph-container");
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
const svgDoc = iframe.contentDocument;
|
||||
const gElements = svgDoc.getElementsByClassName("beatrice-node-pointer");
|
||||
for (const gElement of gElements) {
|
||||
gElement.classList.remove("beatrice-node-pointer-selected");
|
||||
}
|
||||
const keys = beatriceSpeakerIndexInGender.split("-");
|
||||
const id = keys.pop();
|
||||
const gender = keys.pop();
|
||||
|
||||
if (beatriceSpeakerType == gender) {
|
||||
const selected = svgDoc.getElementById(`beatrice-node-${gender}-${id}`);
|
||||
selected?.classList.add("beatrice-node-pointer-selected");
|
||||
}
|
||||
};
|
||||
|
||||
const setBeatriceSpeakerIndex = async (elementId: string) => {
|
||||
setBeatriceSpeakerIndexInGender(elementId);
|
||||
const keys = elementId.split("-");
|
||||
const id = Number(keys.pop());
|
||||
const gender = keys.pop();
|
||||
let beatriceSpeakerIndex;
|
||||
if (gender == "male") {
|
||||
beatriceSpeakerIndex = beatriceMaleSpeakersList[id];
|
||||
} else {
|
||||
beatriceSpeakerIndex = beatriceFemaleSpeakersList[id];
|
||||
}
|
||||
setBeatriceJVSSpeakerId(beatriceSpeakerIndex);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const iframe = document.querySelector(".beatrice-speaker-graph-container");
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
const setOnClick = () => {
|
||||
// @ts-ignore
|
||||
const svgDoc = iframe.contentDocument;
|
||||
|
||||
const gElements = svgDoc.getElementsByClassName("beatrice-node-pointer");
|
||||
const textElements = svgDoc.getElementsByClassName("beatrice-text-pointer");
|
||||
for (const gElement of gElements) {
|
||||
gElement.onclick = () => {
|
||||
setBeatriceSpeakerIndex(gElement.id);
|
||||
};
|
||||
}
|
||||
for (const textElement of textElements) {
|
||||
textElement.onclick = () => {
|
||||
setBeatriceSpeakerIndex(textElement.id);
|
||||
};
|
||||
}
|
||||
setSelectedClass();
|
||||
};
|
||||
iframe.addEventListener("load", setOnClick);
|
||||
return () => {
|
||||
iframe.removeEventListener("load", setOnClick);
|
||||
};
|
||||
}, [selected, beatriceSpeakerType]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedClass();
|
||||
}, [selected, beatriceSpeakerType, beatriceSpeakerIndexInGender]);
|
||||
|
||||
const portrait = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
let portrait;
|
||||
if (webEdition) {
|
||||
const icon = selected.iconFile;
|
||||
portrait = <img className="portrait" src={icon} alt={selected.name} />;
|
||||
} else if (selected.slotIndex == "Beatrice-JVS") {
|
||||
const maleButtonClass = beatriceSpeakerType == "male" ? "button-selected" : "button";
|
||||
const femaleButtonClass = beatriceSpeakerType == "male" ? "button" : "button-selected";
|
||||
const svgURL = beatriceSpeakerType == "male" ? "./assets/beatrice/male-clickable.svg" : "./assets/beatrice/female-clickable.svg";
|
||||
portrait = (
|
||||
<>
|
||||
<div className="beatrice-portrait-title">
|
||||
Beatrice <span className="edition">JVS Corpus</span>
|
||||
</div>
|
||||
<div className="beatrice-portrait-select">
|
||||
<div
|
||||
className={maleButtonClass}
|
||||
onClick={() => {
|
||||
setBeatriceSpeakerType(BeatriceSpeakerType.male);
|
||||
}}
|
||||
>
|
||||
male
|
||||
</div>
|
||||
<div
|
||||
className={femaleButtonClass}
|
||||
onClick={() => {
|
||||
setBeatriceSpeakerType(BeatriceSpeakerType.female);
|
||||
}}
|
||||
>
|
||||
female
|
||||
</div>
|
||||
</div>
|
||||
{/* <iframe className="beatrice-speaker-graph-container" style={{ width: "20rem", height: "20rem", border: "none" }} src="./assets/beatrice/female-clickable.svg" title="terms_of_use" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe> */}
|
||||
<iframe className="beatrice-speaker-graph-container" src={svgURL} title="beatrice JVS Corpus speakers" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
const modelDir = serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS" ? "model_dir_static" : serverSetting.serverSetting.voiceChangerParams.model_dir;
|
||||
const icon = selected.iconFile.length > 0 ? modelDir + "/" + selected.slotIndex + "/" + selected.iconFile.split(/[\/\\]/).pop() : "./assets/icons/human.png";
|
||||
portrait = <img className="portrait" src={icon} alt={selected.name} />;
|
||||
}
|
||||
const selectedTermOfUseUrlLink = selected.termsOfUseUrl ? (
|
||||
<a href={selected.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="portrait-area-terms-of-use-link">
|
||||
[{messageBuilderState.getMessage(__filename, "terms_of_use")}]
|
||||
</a>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="portrait-area">
|
||||
<div className="portrait-container">
|
||||
{portrait}
|
||||
<div className="portrait-area-status">
|
||||
<p>
|
||||
<span className="portrait-area-status-vctype">{selected.voiceChangerType}</span>
|
||||
</p>
|
||||
<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>
|
||||
<p>
|
||||
rtf: <span id="status-rtf">0</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="portrait-area-terms-of-use">{selectedTermOfUseUrlLink}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [selected, beatriceSpeakerType]);
|
||||
|
||||
return portrait;
|
||||
};
|
@ -1,55 +1,103 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
|
||||
export type TuningAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
|
||||
export type TuningAreaProps = {};
|
||||
|
||||
export const TuningArea = (_props: TuningAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting, webInfoState, webEdition } = useAppState();
|
||||
const { setBeatriceJVSSpeakerPitch, beatriceJVSSpeakerPitch } = useGuiState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
if (webEdition) {
|
||||
return webInfoState.webModelslot;
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots, webEdition]);
|
||||
|
||||
const tuningArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
if (selected.voiceChangerType == "MMVCv13" || selected.voiceChangerType == "MMVCv15") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const currentTuning = serverSetting.serverSetting.tran
|
||||
const tranValueUpdatedAction = async (val: number) => {
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, tran: val })
|
||||
// For Beatrice
|
||||
if (selected.slotIndex == "Beatrice-JVS") {
|
||||
const updateBeatriceJVSSpeakerPitch = async (pitch: number) => {
|
||||
setBeatriceJVSSpeakerPitch(pitch);
|
||||
};
|
||||
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="-2"
|
||||
max="2"
|
||||
step="1"
|
||||
value={beatriceJVSSpeakerPitch}
|
||||
onChange={(e) => {
|
||||
updateBeatriceJVSSpeakerPitch(Number(e.target.value));
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{beatriceJVSSpeakerPitch}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let currentTuning;
|
||||
if (webEdition) {
|
||||
currentTuning = webInfoState.upkey;
|
||||
} else {
|
||||
currentTuning = serverSetting.serverSetting.tran;
|
||||
}
|
||||
const tranValueUpdatedAction = async (val: number) => {
|
||||
if (webEdition) {
|
||||
webInfoState.setUpkey(val);
|
||||
} else {
|
||||
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-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>
|
||||
<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, serverSetting.updateServerSettings, selected])
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected, webEdition, webInfoState.upkey]);
|
||||
|
||||
|
||||
return tuningArea
|
||||
}
|
||||
return tuningArea;
|
||||
};
|
||||
|
@ -1,56 +1,59 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
|
||||
export type IndexAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
|
||||
export type IndexAreaProps = {};
|
||||
|
||||
export const IndexArea = (_props: IndexAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting } = useAppState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const indexArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
if (selected.voiceChangerType != "RVC") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const currentIndexRatio = serverSetting.serverSetting.indexRatio
|
||||
const currentIndexRatio = serverSetting.serverSetting.indexRatio;
|
||||
const indexRatioValueUpdatedAction = async (val: number) => {
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, indexRatio: val })
|
||||
}
|
||||
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-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>
|
||||
<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, serverSetting.updateServerSettings, selected])
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
|
||||
|
||||
return indexArea
|
||||
}
|
||||
return indexArea;
|
||||
};
|
||||
|
@ -1,113 +1,112 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
|
||||
export type SpeakerAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
|
||||
export type SpeakerAreaProps = {};
|
||||
|
||||
export const SpeakerArea = (_props: SpeakerAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting } = useAppState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const srcArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (selected.voiceChangerType != "MMVCv13" && selected.voiceChangerType != "MMVCv15") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
const options = Object.keys(selected.speakers).map(key => {
|
||||
const val = selected.speakers[Number(key)]
|
||||
const options = Object.keys(selected.speakers).map((key) => {
|
||||
const val = selected.speakers[Number(key)];
|
||||
return (
|
||||
<option key={key} value={key}>{val}[{key}]</option>
|
||||
)
|
||||
})
|
||||
<option key={key} value={key}>
|
||||
{val}[{key}]
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const srcSpeakerValueUpdatedAction = async (val: number) => {
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, srcId: val })
|
||||
}
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, srcId: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
Voice:
|
||||
</div>
|
||||
<div className="character-area-control-title">Voice:</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-slider-control">
|
||||
<span className="character-area-slider-control-kind">src</span>
|
||||
<span className="character-area-slider-control-slider">
|
||||
<select value={serverSetting.serverSetting.srcId} onChange={(e) => { srcSpeakerValueUpdatedAction(Number(e.target.value)) }}>
|
||||
<select
|
||||
value={serverSetting.serverSetting.srcId}
|
||||
onChange={(e) => {
|
||||
srcSpeakerValueUpdatedAction(Number(e.target.value));
|
||||
}}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected])
|
||||
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
const dstArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
if (selected.slotIndex == "Beatrice-JVS") {
|
||||
return; // beatrice JVS は変換先話者をグラフから選択するので、ここでは表示しない
|
||||
}
|
||||
|
||||
const options = Object.keys(selected.speakers).map(key => {
|
||||
const val = selected.speakers[Number(key)]
|
||||
const options = Object.keys(selected.speakers).map((key) => {
|
||||
const val = selected.speakers[Number(key)];
|
||||
return (
|
||||
<option key={key} value={key}>{val}[{key}]</option>
|
||||
)
|
||||
})
|
||||
<option key={key} value={key}>
|
||||
{val}[{key}]
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const srcSpeakerValueUpdatedAction = async (val: number) => {
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, dstId: val })
|
||||
}
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, dstId: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
{
|
||||
selected.voiceChangerType == "DDSP-SVC" ||
|
||||
selected.voiceChangerType == "so-vits-svc-40" ||
|
||||
selected.voiceChangerType == "RVC" ? "Voice:" : ""
|
||||
}
|
||||
</div>
|
||||
<div className="character-area-control-title">{selected.voiceChangerType == "DDSP-SVC" || selected.voiceChangerType == "so-vits-svc-40" || selected.voiceChangerType == "RVC" ? "Voice:" : ""}</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-slider-control">
|
||||
<span className="character-area-slider-control-kind">
|
||||
{
|
||||
selected.voiceChangerType == "MMVCv13" ||
|
||||
selected.voiceChangerType == "MMVCv15" ? "dst" : ""
|
||||
}
|
||||
|
||||
</span>
|
||||
<span className="character-area-slider-control-kind">{selected.voiceChangerType == "MMVCv13" || selected.voiceChangerType == "MMVCv15" ? "dst" : ""}</span>
|
||||
<span className="character-area-slider-control-slider">
|
||||
<select value={serverSetting.serverSetting.dstId} onChange={(e) => { srcSpeakerValueUpdatedAction(Number(e.target.value)) }}>
|
||||
<select
|
||||
value={serverSetting.serverSetting.dstId}
|
||||
onChange={(e) => {
|
||||
srcSpeakerValueUpdatedAction(Number(e.target.value));
|
||||
}}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected])
|
||||
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{srcArea}
|
||||
{dstArea}
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,72 +1,70 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
import { MMVCv15ModelSlot } from "@dannadori/voice-changer-client-js"
|
||||
|
||||
export type SpeakerAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { MMVCv15ModelSlot } from "@dannadori/voice-changer-client-js";
|
||||
|
||||
export type SpeakerAreaProps = {};
|
||||
|
||||
export const F0FactorArea = (_props: SpeakerAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting } = useAppState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const f0FactorArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (selected.voiceChangerType != "MMVCv15") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
const selectedMMVCv15 = selected as MMVCv15ModelSlot
|
||||
const selectedMMVCv15 = selected as MMVCv15ModelSlot;
|
||||
|
||||
const recommendF0 = (selectedMMVCv15.f0[serverSetting.serverSetting.dstId] / selectedMMVCv15.f0[serverSetting.serverSetting.srcId]).toFixed(2)
|
||||
const recommendF0 = (selectedMMVCv15.f0[serverSetting.serverSetting.dstId] / selectedMMVCv15.f0[serverSetting.serverSetting.srcId]).toFixed(2);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
F0Factor:
|
||||
</div>
|
||||
<div className="character-area-control-title">F0Factor:</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.01" max="5.00" step="0.01" value={serverSetting.serverSetting.f0Factor} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, f0Factor: Number(e.target.value) })
|
||||
}}></input>
|
||||
<input
|
||||
type="range"
|
||||
min="0.01"
|
||||
max="5.00"
|
||||
step="0.01"
|
||||
value={serverSetting.serverSetting.f0Factor}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, f0Factor: Number(e.target.value) });
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{serverSetting.serverSetting.f0Factor}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
|
||||
</div>
|
||||
<div className="character-area-control-title"></div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-slider-control">
|
||||
<span className="character-area-slider-control-text">recommend:</span>
|
||||
<span className="character-area-slider-control-text">
|
||||
{recommendF0}
|
||||
</span>
|
||||
<span className="character-area-slider-control-text">{recommendF0}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected])
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
|
||||
return f0FactorArea
|
||||
}
|
||||
return f0FactorArea;
|
||||
};
|
||||
|
@ -1,81 +1,86 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
|
||||
export type SoVitsSVC40SettingAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
|
||||
export type SoVitsSVC40SettingAreaProps = {};
|
||||
|
||||
export const SoVitsSVC40SettingArea = (_props: SoVitsSVC40SettingAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting } = useAppState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const settingArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (selected.voiceChangerType != "so-vits-svc-40") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const cluster = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
Cluster:
|
||||
</div>
|
||||
<div className="character-area-control-title">Cluster:</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.0" step="0.1" value={serverSetting.serverSetting.clusterInferRatio} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, clusterInferRatio: Number(e.target.value) })
|
||||
}}></input>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.1"
|
||||
value={serverSetting.serverSetting.clusterInferRatio}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, clusterInferRatio: Number(e.target.value) });
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{serverSetting.serverSetting.clusterInferRatio}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
const noise = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
Noise:
|
||||
</div>
|
||||
<div className="character-area-control-title">Noise:</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.0" step="0.1" value={serverSetting.serverSetting.noiseScale} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, noiseScale: Number(e.target.value) })
|
||||
}}></input>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.1"
|
||||
value={serverSetting.serverSetting.noiseScale}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, noiseScale: Number(e.target.value) });
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{serverSetting.serverSetting.noiseScale}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{cluster}
|
||||
{noise}
|
||||
</>
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected])
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
|
||||
return settingArea
|
||||
}
|
||||
return settingArea;
|
||||
};
|
||||
|
@ -1,81 +1,86 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
|
||||
export type DDSPSVC30SettingAreaProps = {
|
||||
}
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
|
||||
export type DDSPSVC30SettingAreaProps = {};
|
||||
|
||||
export const DDSPSVC30SettingArea = (_props: DDSPSVC30SettingAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { serverSetting } = useAppState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||
|
||||
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const settingArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (selected.voiceChangerType != "DDSP-SVC") {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const acc = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
ACC:
|
||||
</div>
|
||||
<div className="character-area-control-title">ACC:</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="1" max="20" step="1" value={serverSetting.serverSetting.diffAcc} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, diffAcc: Number(e.target.value) })
|
||||
}}></input>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="20"
|
||||
step="1"
|
||||
value={serverSetting.serverSetting.diffAcc}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, diffAcc: Number(e.target.value) });
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{serverSetting.serverSetting.diffAcc}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
const kstep = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">
|
||||
Kstep:
|
||||
</div>
|
||||
<div className="character-area-control-title">Kstep:</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="21" max="300" step="1" value={serverSetting.serverSetting.kStep} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, kStep: Number(e.target.value) })
|
||||
}}></input>
|
||||
<input
|
||||
type="range"
|
||||
min="21"
|
||||
max="300"
|
||||
step="1"
|
||||
value={serverSetting.serverSetting.kStep}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, kStep: Number(e.target.value) });
|
||||
}}
|
||||
></input>
|
||||
</span>
|
||||
<span className="character-area-slider-control-val">{serverSetting.serverSetting.kStep}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{acc}
|
||||
{kstep}
|
||||
</>
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected])
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected]);
|
||||
|
||||
|
||||
return settingArea
|
||||
}
|
||||
return settingArea;
|
||||
};
|
||||
|
@ -10,8 +10,12 @@ export const DiffusionSVCSettingArea = (_props: DiffusionSVCSettingAreaProps) =>
|
||||
const selected = useMemo(() => {
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots]);
|
||||
|
||||
const settingArea = useMemo(() => {
|
||||
|
@ -0,0 +1,199 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
|
||||
export type WebEditionSettingAreaProps = {};
|
||||
|
||||
export const WebEditionSettingArea = (_props: WebEditionSettingAreaProps) => {
|
||||
const { serverSetting, webInfoState, webEdition } = useAppState();
|
||||
const guiState = useGuiState();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (webEdition) {
|
||||
return webInfoState.webModelslot;
|
||||
}
|
||||
return null;
|
||||
}, [webEdition]);
|
||||
|
||||
const settingArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const readyForConfig = guiState.isConverting == false && webInfoState.webModelLoadingState == "ready";
|
||||
|
||||
const versionV1ClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv1" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const versionV2ClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv2" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const verison = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">Version</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-control-buttons">
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : versionV1ClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv1" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig("rvcv1", webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
v1
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : versionV2ClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv2" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig("rvcv2", webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
v2
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const sr16KClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.sampleRate == "16k" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const sr32KClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.sampleRate == "32k" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const sr40KClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.sampleRate == "40k" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const sampleRate = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">SR</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-control-buttons">
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : sr16KClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.sampleRate == "16k" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig("rvcv2", "16k", webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
16k
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : sr32KClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.sampleRate == "32k" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, "32k", webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
32k
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : sr40KClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.sampleRate == "40k" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, "40k", webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
40k
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const pitchEnableClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.useF0 == true ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const pitchDisableClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.useF0 == false ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const pitch = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">Pitch</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-control-buttons">
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : pitchEnableClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.useF0 == true || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, true, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
Enable
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : pitchDisableClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.useF0 == false || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, false, webInfoState.voiceChangerConfig.inputLength);
|
||||
}}
|
||||
>
|
||||
Disable
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const latencyHighClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "24000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const latencyMidClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "12000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const latencyLowClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "8000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
|
||||
const latency = (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">Latency</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-control-buttons">
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : latencyHighClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.inputLength == "24000" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "24000");
|
||||
}}
|
||||
>
|
||||
High
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : latencyMidClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.inputLength == "12000" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "12000");
|
||||
}}
|
||||
>
|
||||
Mid
|
||||
</span>
|
||||
<span
|
||||
className={!readyForConfig ? "character-area-control-button-disable" : latencyLowClassName}
|
||||
onClick={() => {
|
||||
if (webInfoState.voiceChangerConfig.inputLength == "8000" || !readyForConfig) return;
|
||||
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "8000");
|
||||
}}
|
||||
>
|
||||
Low
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{verison}
|
||||
{sampleRate}
|
||||
{pitch}
|
||||
{latency}
|
||||
</>
|
||||
);
|
||||
}, [
|
||||
serverSetting.serverSetting,
|
||||
serverSetting.updateServerSettings,
|
||||
selected,
|
||||
webInfoState.upkey,
|
||||
webInfoState.voiceChangerConfig.config.voiceChangerType,
|
||||
webInfoState.voiceChangerConfig.sampleRate,
|
||||
webInfoState.voiceChangerConfig.useF0,
|
||||
webInfoState.voiceChangerConfig.inputLength,
|
||||
webInfoState.webModelLoadingState,
|
||||
guiState.isConverting,
|
||||
webInfoState.webModelLoadingState,
|
||||
]);
|
||||
|
||||
return settingArea;
|
||||
};
|
@ -10,77 +10,38 @@ import { F0FactorArea } from "./101-4_F0FactorArea";
|
||||
import { SoVitsSVC40SettingArea } from "./101-5_so-vits-svc40SettingArea";
|
||||
import { DDSPSVC30SettingArea } from "./101-6_ddsp-svc30SettingArea";
|
||||
import { DiffusionSVCSettingArea } from "./101-7_diffusion-svcSettingArea";
|
||||
import { Portrait } from "./101-0_Portrait";
|
||||
import { useAppRoot } from "../../../001_provider/001_AppRootProvider";
|
||||
import { WebEditionSettingArea } from "./101-8_web-editionSettingArea";
|
||||
|
||||
export type CharacterAreaProps = {};
|
||||
|
||||
export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
const { serverSetting, initializedRef, volume, bufferingTime, performance, setting, setVoiceChangerClientSetting, start, stop } = useAppState();
|
||||
const { appGuiSettingState } = useAppRoot();
|
||||
const { serverSetting, initializedRef, setting, setVoiceChangerClientSetting, start, stop, webInfoState } = useAppState();
|
||||
const guiState = useGuiState();
|
||||
const messageBuilderState = useMessageBuilder();
|
||||
|
||||
const webEdition = appGuiSettingState.edition.indexOf("web") >= 0;
|
||||
const { beatriceJVSSpeakerId } = useGuiState();
|
||||
useMemo(() => {
|
||||
messageBuilderState.setMessage(__filename, "terms_of_use", { ja: "利用規約", en: "terms of use" });
|
||||
messageBuilderState.setMessage(__filename, "export_to_onnx", { ja: "onnx出力", en: "export to onnx" });
|
||||
messageBuilderState.setMessage(__filename, "save_default", { ja: "設定保存", en: "save setting" });
|
||||
messageBuilderState.setMessage(__filename, "alert_onnx", { ja: "ボイチェン中はonnx出力できません", en: "cannot export onnx when voice conversion is enabled" });
|
||||
}, []);
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if (webEdition) {
|
||||
return webInfoState.webModelslot;
|
||||
}
|
||||
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||
return;
|
||||
} else if (serverSetting.serverSetting.modelSlotIndex == "Beatrice-JVS") {
|
||||
const beatriceJVS = serverSetting.serverSetting.modelSlots.find((v) => v.slotIndex == "Beatrice-JVS");
|
||||
return beatriceJVS;
|
||||
} else {
|
||||
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex];
|
||||
}
|
||||
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 ? serverSetting.serverSetting.voiceChangerParams.model_dir + "/" + selected.slotIndex + "/" + selected.iconFile.split(/[\/\\]/).pop() : "./assets/icons/human.png";
|
||||
const selectedTermOfUseUrlLink = selected.termsOfUseUrl ? (
|
||||
<a href={selected.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="portrait-area-terms-of-use-link">
|
||||
[{messageBuilderState.getMessage(__filename, "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>
|
||||
<span className="portrait-area-status-vctype">{selected.voiceChangerType}</span>
|
||||
</p>
|
||||
<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]);
|
||||
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots, webEdition]);
|
||||
|
||||
const [startWithAudioContextCreate, setStartWithAudioContextCreate] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
@ -91,6 +52,22 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
start();
|
||||
}, [startWithAudioContextCreate]);
|
||||
|
||||
const nameArea = useMemo(() => {
|
||||
if (!selected) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">Name:</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-text">
|
||||
{selected.name} {selected.slotIndex == "Beatrice-JVS" ? `speaker:${beatriceJVSSpeakerId}` : ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [selected, beatriceJVSSpeakerId]);
|
||||
|
||||
const startControl = useMemo(() => {
|
||||
const onStartClicked = async () => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
@ -137,23 +114,67 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
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";
|
||||
const passThruClassName = serverSetting.serverSetting.passThrough == false ? "character-area-control-passthru-button-stanby" : "character-area-control-passthru-button-active blinking";
|
||||
console.log("serverSetting.serverSetting.passThrough", passThruClassName, serverSetting.serverSetting.passThrough);
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-buttons">
|
||||
<div onClick={onStartClicked} className={startClassName}>
|
||||
start
|
||||
|
||||
if (webEdition && webInfoState.webModelLoadingState != "ready") {
|
||||
if (webInfoState.webModelLoadingState == "none" || webInfoState.webModelLoadingState == "loading") {
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">wait...</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-text blink">{webInfoState.webModelLoadingState}..</div>
|
||||
<div className="character-area-text">
|
||||
pre:{Math.floor(webInfoState.progressLoadPreprocess * 100)}%, model: {Math.floor(webInfoState.progressLoadVCModel * 100)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div onClick={onStopClicked} className={stopClassName}>
|
||||
stop
|
||||
);
|
||||
} else if (webInfoState.webModelLoadingState == "warmup") {
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
<div className="character-area-control-title">wait...</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-text blink">{webInfoState.webModelLoadingState}..</div>
|
||||
<div className="character-area-text">warm up:{Math.floor(webInfoState.progressWarmup * 100)}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div onClick={onPassThroughClicked} className={passThruClassName}>
|
||||
passthru
|
||||
);
|
||||
} else {
|
||||
throw new Error("invalid webModelLoadingState");
|
||||
}
|
||||
} else {
|
||||
if (webEdition) {
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [guiState.isConverting, start, stop, serverSetting.serverSetting, serverSetting.updateServerSettings]);
|
||||
);
|
||||
} else {
|
||||
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 onClick={onPassThroughClicked} className={passThruClassName}>
|
||||
passthru
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [guiState.isConverting, start, stop, serverSetting.serverSetting, serverSetting.updateServerSettings, webInfoState.progressLoadPreprocess, webInfoState.progressLoadVCModel, webInfoState.progressWarmup, webInfoState.webModelLoadingState]);
|
||||
|
||||
const gainControl = useMemo(() => {
|
||||
const currentInputGain = serverSetting.serverSetting.enableServerAudio == 0 ? setting.voiceChangerClientSetting.inputGain : serverSetting.serverSetting.serverInputAudioGain;
|
||||
@ -222,6 +243,9 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
if (!selected) {
|
||||
return <></>;
|
||||
}
|
||||
if (webEdition) {
|
||||
return <></>;
|
||||
}
|
||||
const onUpdateDefaultClicked = async () => {
|
||||
await serverSetting.updateModelDefault();
|
||||
};
|
||||
@ -270,8 +294,9 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
const characterArea = useMemo(() => {
|
||||
return (
|
||||
<div className="character-area">
|
||||
{portrait}
|
||||
<Portrait></Portrait>
|
||||
<div className="character-area-control-area">
|
||||
{nameArea}
|
||||
{startControl}
|
||||
{gainControl}
|
||||
<TuningArea />
|
||||
@ -281,11 +306,12 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
<SoVitsSVC40SettingArea />
|
||||
<DDSPSVC30SettingArea />
|
||||
<DiffusionSVCSettingArea />
|
||||
<WebEditionSettingArea />
|
||||
{modelSlotControl}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [portrait, startControl, gainControl, modelSlotControl]);
|
||||
}, [startControl, gainControl, modelSlotControl]);
|
||||
|
||||
return characterArea;
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ export type QualityAreaProps = {
|
||||
};
|
||||
|
||||
export const QualityArea = (props: QualityAreaProps) => {
|
||||
const { setVoiceChangerClientSetting, serverSetting, setting } = useAppState();
|
||||
const { setVoiceChangerClientSetting, serverSetting, setting, webEdition } = useAppState();
|
||||
const { appGuiSettingState } = useAppRoot();
|
||||
const edition = appGuiSettingState.edition;
|
||||
|
||||
@ -47,6 +47,52 @@ export const QualityArea = (props: QualityAreaProps) => {
|
||||
};
|
||||
const f0DetOptions = generateF0DetOptions();
|
||||
|
||||
const f0Det = webEdition ? (
|
||||
<></>
|
||||
) : (
|
||||
<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 });
|
||||
}}
|
||||
>
|
||||
{f0DetOptions}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const threshold = webEdition ? (
|
||||
<></>
|
||||
) : (
|
||||
<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>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
<div className="config-sub-area-control">
|
||||
@ -101,42 +147,8 @@ export const QualityArea = (props: QualityAreaProps) => {
|
||||
</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 });
|
||||
}}
|
||||
>
|
||||
{f0DetOptions}
|
||||
</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>
|
||||
{f0Det}
|
||||
{threshold}
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting, setting, serverSetting.updateServerSettings, setVoiceChangerClientSetting]);
|
||||
|
@ -7,13 +7,14 @@ export type ConvertProps = {
|
||||
};
|
||||
|
||||
export const ConvertArea = (props: ConvertProps) => {
|
||||
const { setting, serverSetting, setWorkletNodeSetting, trancateBuffer } = useAppState();
|
||||
const { setting, serverSetting, setWorkletNodeSetting, trancateBuffer, webEdition } = useAppState();
|
||||
const { appGuiSettingState } = useAppRoot();
|
||||
const edition = appGuiSettingState.edition;
|
||||
|
||||
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];
|
||||
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, 4096, 8192, 16384];
|
||||
} else {
|
||||
nums = props.inputChunkNums;
|
||||
}
|
||||
@ -110,6 +111,8 @@ export const ConvertArea = (props: ConvertProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : webEdition ? (
|
||||
<></>
|
||||
) : (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title">GPU:</div>
|
||||
@ -133,6 +136,32 @@ export const ConvertArea = (props: ConvertProps) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraArea = webEdition ? (
|
||||
<></>
|
||||
) : (
|
||||
<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) });
|
||||
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>
|
||||
);
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
<div className="config-sub-area-control">
|
||||
@ -157,27 +186,7 @@ export const ConvertArea = (props: ConvertProps) => {
|
||||
</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) });
|
||||
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>
|
||||
{extraArea}
|
||||
{gpuSelect}
|
||||
</div>
|
||||
);
|
||||
|
@ -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_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 { AUDIO_ELEMENT_FOR_PLAY_MONITOR, AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK, 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, setAudioMonitorElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording } = useAppState();
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, setAudioMonitorForGUI, audioOutputForGUI, audioMonitorForGUI, outputAudioDeviceInfo, shareScreenEnabled, setShareScreenEnabled } = useGuiState();
|
||||
const { setting, serverSetting, audioContext, setAudioOutputElementId, setAudioMonitorElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording, webEdition } = useAppState();
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, setAudioMonitorForGUI, audioOutputForGUI, audioMonitorForGUI, outputAudioDeviceInfo, shareScreenEnabled, setShareScreenEnabled, reloadDeviceInfo } = useGuiState();
|
||||
const [inputHostApi, setInputHostApi] = useState<string>("ALL");
|
||||
const [outputHostApi, setOutputHostApi] = useState<string>("ALL");
|
||||
const [monitorHostApi, setMonitorHostApi] = useState<string>("ALL");
|
||||
@ -21,6 +21,20 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
|
||||
// (1) Audio Mode
|
||||
const deviceModeRow = useMemo(() => {
|
||||
if (webEdition) {
|
||||
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-buttons">
|
||||
<div onClick={reloadDeviceInfo} className="config-sub-area-button">
|
||||
reload
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const enableServerAudio = serverSetting.serverSetting.enableServerAudio;
|
||||
const clientChecked = enableServerAudio == 1 ? false : true;
|
||||
const serverChecked = enableServerAudio == 1 ? true : false;
|
||||
@ -63,6 +77,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
/>
|
||||
<label htmlFor="server-device">server</label>
|
||||
</div>
|
||||
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={reloadDeviceInfo} className="config-sub-area-button">
|
||||
reload
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -389,8 +409,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
// Server Audio を使う場合はElementから音は出さない。
|
||||
audio.volume = 0;
|
||||
} else if (audioOutputForGUI == "none") {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
try {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||
audio.volume = 0;
|
||||
} else {
|
||||
@ -404,8 +428,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
return x.deviceId == audioOutputForGUI;
|
||||
});
|
||||
if (found) {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioOutputForGUI);
|
||||
try {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioOutputForGUI);
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
@ -620,9 +648,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
// Server Audio を使う場合はElementから音は出さない。
|
||||
audio.volume = 0;
|
||||
} else if (audioMonitorForGUI == "none") {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
audio.volume = 0;
|
||||
try {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
audio.volume = 0;
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
||||
return x.kind == "audiooutput";
|
||||
@ -631,9 +663,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
return x.deviceId == audioMonitorForGUI;
|
||||
});
|
||||
if (found) {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioMonitorForGUI);
|
||||
audio.volume = 1;
|
||||
try {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioMonitorForGUI);
|
||||
audio.volume = 1;
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
|
@ -1,44 +1,52 @@
|
||||
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"
|
||||
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 type RecorderAreaProps = {};
|
||||
|
||||
export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState()
|
||||
|
||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false)
|
||||
|
||||
const { serverSetting, webEdition } = useAppState();
|
||||
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState();
|
||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false);
|
||||
|
||||
const serverIORecorderRow = useMemo(() => {
|
||||
const onServerIORecordStartClicked = async () => {
|
||||
setServerIORecording(true)
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 })
|
||||
if (webEdition) {
|
||||
return <> </>;
|
||||
}
|
||||
const onServerIORecordStartClicked = async () => {
|
||||
setServerIORecording(true);
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 });
|
||||
};
|
||||
const onServerIORecordStopClicked = async () => {
|
||||
setServerIORecording(false)
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 })
|
||||
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)
|
||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement;
|
||||
wavInput.src = "/tmp/in.wav?" + new Date().getTime();
|
||||
wavInput.controls = true;
|
||||
try {
|
||||
// @ts-ignore
|
||||
wavInput.setSinkId(audioOutputForAnalyzer);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
// 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 wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement;
|
||||
wavOutput.src = "/tmp/out.wav?" + new Date().getTime();
|
||||
wavOutput.controls = true;
|
||||
try {
|
||||
// @ts-ignore
|
||||
wavOutput.setSinkId(audioOutputForAnalyzer);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button"
|
||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active"
|
||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button";
|
||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active";
|
||||
return (
|
||||
<>
|
||||
<div className="config-sub-area-control">
|
||||
@ -49,34 +57,51 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
<div className="config-sub-area-control-title">SIO rec.</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onServerIORecordStartClicked} className={startClassName}>start</div>
|
||||
<div onClick={onServerIORecordStopClicked} className={stopClassName}>stop</div>
|
||||
<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">output</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 => {
|
||||
<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;
|
||||
try {
|
||||
//@ts-ignore
|
||||
wavInput.setSinkId(e.target.value);
|
||||
//@ts-ignore
|
||||
wavOutput.setSinkId(e.target.value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{outputAudioDeviceInfo
|
||||
.map((x) => {
|
||||
if (x.deviceId == "none") {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||
}).filter(x => { return x != null })
|
||||
}
|
||||
return (
|
||||
<option key={x.deviceId} value={x.deviceId}>
|
||||
{x.label}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
.filter((x) => {
|
||||
return x != null;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,17 +127,9 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings])
|
||||
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
{serverIORecorderRow}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
);
|
||||
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings]);
|
||||
|
||||
return <div className="config-sub-area">{serverIORecorderRow}</div>;
|
||||
};
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
|
||||
export type MoreActionAreaProps = {};
|
||||
|
||||
export const MoreActionArea = (_props: MoreActionAreaProps) => {
|
||||
const { stateControls } = useGuiState();
|
||||
const { webEdition } = useAppState();
|
||||
|
||||
const serverIORecorderRow = useMemo(() => {
|
||||
const onOpenMergeLabClicked = () => {
|
||||
@ -44,5 +46,9 @@ export const MoreActionArea = (_props: MoreActionAreaProps) => {
|
||||
);
|
||||
}, [stateControls]);
|
||||
|
||||
return <div className="config-sub-area">{serverIORecorderRow}</div>;
|
||||
if (webEdition) {
|
||||
return <> </>;
|
||||
} else {
|
||||
return <div className="config-sub-area">{serverIORecorderRow}</div>;
|
||||
}
|
||||
};
|
||||
|
@ -1124,6 +1124,7 @@ body {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* ################## */
|
||||
@ -1321,7 +1322,7 @@ body {
|
||||
background: rgba(100, 100, 100, 0.5);
|
||||
color: white;
|
||||
position: absolute;
|
||||
paddig: 2px;
|
||||
padding: 2px;
|
||||
font-size: 0.7rem;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
@ -1363,6 +1364,15 @@ body {
|
||||
border: solid 1px #000;
|
||||
}
|
||||
}
|
||||
.character-area-control-button-disable {
|
||||
width: 5rem;
|
||||
border: solid 1px #333;
|
||||
border-radius: 2px;
|
||||
background: #d3d7d3;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
color: grey;
|
||||
}
|
||||
.character-area-control-passthru-button-stanby {
|
||||
width: 5rem;
|
||||
border: solid 1px #999;
|
||||
@ -1402,6 +1412,9 @@ body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.character-area-text {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.character-area-slider-control {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -1831,3 +1844,62 @@ audio::-webkit-media-controls-overlay-enclosure{
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation: blinking 0.8s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes blinking {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.beatrice-portrait-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
text-shadow: 0 0 2px #333;
|
||||
text-align: center;
|
||||
.edition {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
}
|
||||
.beatrice-portrait-select {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.button {
|
||||
/* border: solid 2px #999; */
|
||||
color: #615454;
|
||||
font-weight: 700;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 2px;
|
||||
background: #adafad;
|
||||
cursor: pointer;
|
||||
padding: 0px 5px 0px 5px;
|
||||
margin: 0px 5px 0px 5px;
|
||||
line-height: 140%;
|
||||
height: 1.1rem;
|
||||
}
|
||||
.button-selected {
|
||||
/* border: solid 2px #999; */
|
||||
color: #615454;
|
||||
font-weight: 700;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 2px;
|
||||
background: #62b574;
|
||||
cursor: pointer;
|
||||
padding: 0px 5px 0px 5px;
|
||||
margin: 0px 5px 0px 5px;
|
||||
line-height: 140%;
|
||||
height: 1.1rem;
|
||||
}
|
||||
}
|
||||
.beatrice-speaker-graph-container {
|
||||
width: 20rem;
|
||||
height: 19rem;
|
||||
border: none;
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
const webpack = require("webpack");
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
entry: "./src/000_index.tsx",
|
||||
resolve: {
|
||||
extensions: [".ts", ".tsx", ".js"],
|
||||
fallback: {
|
||||
buffer: require.resolve("buffer/"),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -29,7 +33,7 @@ module.exports = {
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", { loader: "css-loader", options: { importLoaders: 1 } }, "postcss-loader"],
|
||||
},
|
||||
|
||||
{ test: /\.json$/, type: "asset/inline" },
|
||||
],
|
||||
},
|
||||
output: {
|
||||
@ -37,6 +41,9 @@ module.exports = {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, "public/index.html"),
|
||||
filename: "./index.html",
|
||||
@ -47,5 +54,45 @@ module.exports = {
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "public/favicon.ico", to: "favicon.ico" }],
|
||||
}),
|
||||
]
|
||||
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/ort-wasm-simd.wasm", to: "ort-wasm-simd.wasm" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/tfjs-backend-wasm-simd.wasm", to: "tfjs-backend-wasm-simd.wasm" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/process.js", to: "process.js" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_emb_pit_24000.bin", to: "models/rvcv2_emb_pit_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_32k_f0_24000.bin", to: "models/rvcv2_amitaro_v2_32k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_32k_nof0_24000.bin", to: "models/rvcv2_amitaro_v2_32k_nof0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_40k_f0_24000.bin", to: "models/rvcv2_amitaro_v2_40k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_40k_nof0_24000.bin", to: "models/rvcv2_amitaro_v2_40k_nof0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_emb_pit_24000.bin", to: "models/rvcv1_emb_pit_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_32k_f0_24000.bin", to: "models/rvcv1_amitaro_v1_32k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_32k_nof0_24000.bin", to: "models/rvcv1_amitaro_v1_32k_nof0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_40k_f0_24000.bin", to: "models/rvcv1_amitaro_v1_40k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_40k_nof0_24000.bin", to: "models/rvcv1_amitaro_v1_40k_nof0_24000.bin" }],
|
||||
// }),
|
||||
],
|
||||
};
|
||||
|
111
client/demo/webpack_web.common.js
Normal file
111
client/demo/webpack_web.common.js
Normal file
@ -0,0 +1,111 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
const webpack = require("webpack");
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
entry: "./src/000_index.tsx",
|
||||
resolve: {
|
||||
extensions: [".ts", ".tsx", ".js"],
|
||||
fallback: {
|
||||
buffer: require.resolve("buffer/"),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: [/\.ts$/, /\.tsx$/],
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
|
||||
plugins: ["@babel/plugin-transform-runtime"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: "html-loader",
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", { loader: "css-loader", options: { importLoaders: 1 } }, "postcss-loader"],
|
||||
},
|
||||
{ test: /\.json$/, type: "asset/inline" },
|
||||
{ test: /\.svg$/, type: "asset/resource" },
|
||||
],
|
||||
},
|
||||
output: {
|
||||
filename: "index.js",
|
||||
path: path.resolve(__dirname, "dist_web"),
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, "public/index.html"),
|
||||
filename: "./index.html",
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "public/assets", to: "assets" }],
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "public/favicon.ico", to: "favicon.ico" }],
|
||||
}),
|
||||
|
||||
// ダミーファイルコピー
|
||||
// new CopyPlugin({ //コピーの順番で上のassetのコピーで上書きされることがあるようだ。⇒npmスクリプトで対処。
|
||||
// patterns: [{ from: "public/assets/gui_settings/edition_web.txt", to: "assets/gui_settings/edition.txt" }],
|
||||
// }),
|
||||
// new CopyPlugin({ // 拡張子なしのファイルコピーはできないようだ。⇒npmスクリプトで対処。
|
||||
// patterns: [{ from: "public/info_web.txt", to: "info" }],
|
||||
// }),
|
||||
|
||||
// VC用ファイルコピー
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/ort-wasm-simd.wasm", to: "ort-wasm-simd.wasm" }],
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/tfjs-backend-wasm-simd.wasm", to: "tfjs-backend-wasm-simd.wasm" }],
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "./node_modules/@dannadori/voice-changer-js/dist/process.js", to: "process.js" }],
|
||||
}),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_emb_pit_24000.bin", to: "models/rvcv2_emb_pit_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_32k_f0_24000.bin", to: "models/rvcv2_amitaro_v2_32k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_32k_nof0_24000.bin", to: "models/rvcv2_amitaro_v2_32k_nof0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_40k_f0_24000.bin", to: "models/rvcv2_amitaro_v2_40k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv2_amitaro_v2_40k_nof0_24000.bin", to: "models/rvcv2_amitaro_v2_40k_nof0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_emb_pit_24000.bin", to: "models/rvcv1_emb_pit_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_32k_f0_24000.bin", to: "models/rvcv1_amitaro_v1_32k_f0_24000.bin" }],
|
||||
// }),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/rvcv1_amitaro_v1_32k_nof0_24000.bin", to: "models/rvcv1_amitaro_v1_32k_nof0_24000.bin" }],
|
||||
// }),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "public/models/rvcv2_exp_v2_32k_f0_24000.bin", to: "models/rvcv2_exp_v2_32k_f0_24000.bin" }],
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "public/models/rvcv2_vctk_v2_16k_f0_24000.bin", to: "models/rvcv2_vctk_v2_16k_f0_24000.bin" }],
|
||||
}),
|
||||
// new CopyPlugin({
|
||||
// patterns: [{ from: "public/models/amitaro.png", to: "models/amitaro.png" }],
|
||||
// }),
|
||||
],
|
||||
};
|
36
client/demo/webpack_web.dev.js
Normal file
36
client/demo/webpack_web.dev.js
Normal file
@ -0,0 +1,36 @@
|
||||
const path = require("path");
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack_web.common.js");
|
||||
const express = require("express");
|
||||
module.exports = merge(common, {
|
||||
mode: "development",
|
||||
devServer: {
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
if (!devServer) {
|
||||
throw new Error("webpack-dev-server is not defined");
|
||||
}
|
||||
|
||||
// ミドルウェアを追加して静的ファイルへのアクセスログを出力
|
||||
devServer.app.use(
|
||||
"/",
|
||||
express.static(path.join(__dirname, "dist_web"), {
|
||||
setHeaders: (res, filepath) => {
|
||||
console.log(`Serving static file: ${filepath}`);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// 既存のミドルウェアをそのまま利用
|
||||
return middlewares;
|
||||
},
|
||||
client: {
|
||||
overlay: {
|
||||
errors: false,
|
||||
warnings: false,
|
||||
},
|
||||
logging: "log",
|
||||
},
|
||||
host: "0.0.0.0",
|
||||
https: true,
|
||||
},
|
||||
});
|
6
client/demo/webpack_web.prod.js
Normal file
6
client/demo/webpack_web.prod.js
Normal file
@ -0,0 +1,6 @@
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack_web.common.js");
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: "production",
|
||||
});
|
11
client/lib/.vscode/settings.json
vendored
Normal file
11
client/lib/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"tab.activeBackground": "#65952acc"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.printWidth": 1024,
|
||||
"prettier.tabWidth": 4,
|
||||
"files.associations": {
|
||||
"*.css": "postcss"
|
||||
}
|
||||
}
|
20789
client/lib/package-lock.json
generated
20789
client/lib/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,58 +1,60 @@
|
||||
{
|
||||
"name": "@dannadori/voice-changer-client-js",
|
||||
"version": "1.0.172",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"scripts": {
|
||||
"clean:worklet": "rimraf worklet/dist/",
|
||||
"webpack:worklet:dev": "webpack --config webpack.worklet.dev.js",
|
||||
"webpack:worklet:prod": "webpack --config webpack.worklet.prod.js",
|
||||
"build:worklet:dev": "npm-run-all clean:worklet webpack:worklet:dev",
|
||||
"build:worklet:prod": "npm-run-all clean:worklet webpack:worklet:prod",
|
||||
"clean": "rimraf dist/",
|
||||
"webpack:dev": "webpack --config webpack.dev.js",
|
||||
"webpack:prod": "webpack --config webpack.prod.js",
|
||||
"build:dev": "npm-run-all build:worklet:dev clean webpack:dev",
|
||||
"build:prod": "npm-run-all build:worklet:prod clean webpack:prod",
|
||||
"release": "npm version patch && npm publish --access=public",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"voice conversion"
|
||||
],
|
||||
"author": "wataru.okada@flect.co.jp",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/audioworklet": "^0.0.50",
|
||||
"@types/node": "^20.6.3",
|
||||
"@types/react": "18.2.22",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.0.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^5.0.1",
|
||||
"ts-loader": "^9.4.4",
|
||||
"typescript": "^5.2.2",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/readable-stream": "^4.0.2",
|
||||
"amazon-chime-sdk-js": "^3.17.0",
|
||||
"buffer": "^6.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"protobufjs": "^7.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"socket.io-client": "^4.7.2"
|
||||
}
|
||||
"name": "@dannadori/voice-changer-client-js",
|
||||
"version": "1.0.182",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"scripts": {
|
||||
"clean:worklet": "rimraf worklet/dist/",
|
||||
"webpack:worklet:dev": "webpack --config webpack.worklet.dev.js",
|
||||
"webpack:worklet:prod": "webpack --config webpack.worklet.prod.js",
|
||||
"build:worklet:dev": "npm-run-all clean:worklet webpack:worklet:dev",
|
||||
"build:worklet:prod": "npm-run-all clean:worklet webpack:worklet:prod",
|
||||
"clean": "rimraf dist/",
|
||||
"webpack:dev": "webpack --config webpack.dev.js",
|
||||
"webpack:prod": "webpack --config webpack.prod.js",
|
||||
"build:dev": "npm-run-all build:worklet:dev clean webpack:dev",
|
||||
"build:prod": "npm-run-all build:worklet:prod clean webpack:prod",
|
||||
"release": "npm version patch && npm publish --access=public",
|
||||
"test": "jest"
|
||||
},
|
||||
"keywords": [
|
||||
"voice conversion"
|
||||
],
|
||||
"author": "wataru.okada@flect.co.jp",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/audioworklet": "^0.0.54",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.21",
|
||||
"@types/react": "18.2.60",
|
||||
"@types/react-dom": "18.2.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.3.3",
|
||||
"webpack": "^5.90.3",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/readable-stream": "^4.0.10",
|
||||
"amazon-chime-sdk-js": "^3.20.0",
|
||||
"buffer": "^6.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"protobufjs": "^7.2.6",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"socket.io-client": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ export declare const RequestType: {
|
||||
readonly stop: "stop";
|
||||
readonly trancateBuffer: "trancateBuffer";
|
||||
};
|
||||
export type RequestType = typeof RequestType[keyof typeof RequestType];
|
||||
export type RequestType = (typeof RequestType)[keyof typeof RequestType];
|
||||
export declare const ResponseType: {
|
||||
readonly volume: "volume";
|
||||
readonly inputData: "inputData";
|
||||
readonly start_ok: "start_ok";
|
||||
readonly stop_ok: "stop_ok";
|
||||
};
|
||||
export type ResponseType = typeof ResponseType[keyof typeof ResponseType];
|
||||
export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType];
|
||||
export type VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: RequestType;
|
||||
voice: Float32Array;
|
||||
|
@ -1,92 +1,90 @@
|
||||
import { VoiceChangerWorkletNode, VoiceChangerWorkletListener } from "./VoiceChangerWorkletNode";
|
||||
import { VoiceChangerWorkletNode, VoiceChangerWorkletListener, InternalCallback } from "./client/VoiceChangerWorkletNode";
|
||||
// @ts-ignore
|
||||
import workerjs from "raw-loader!../worklet/dist/index.js";
|
||||
import { VoiceFocusDeviceTransformer, VoiceFocusTransformDevice } from "amazon-chime-sdk-js";
|
||||
import { createDummyMediaStream, validateUrl } from "./util";
|
||||
import { DefaultClientSettng, MergeModelRequest, ServerSettingKey, VoiceChangerClientSetting, WorkletNodeSetting, WorkletSetting } from "./const";
|
||||
import { ServerConfigurator } from "./ServerConfigurator";
|
||||
import { ServerConfigurator } from "./client/ServerConfigurator";
|
||||
|
||||
// オーディオデータの流れ
|
||||
// input node(mic or MediaStream) -> [vf node] -> [vc node] ->
|
||||
// input node(mic or MediaStream) -> [vf node] -> [vc node] ->
|
||||
// sio/rest server -> [vc node] -> output node
|
||||
|
||||
import { BlockingQueue } from "./utils/BlockingQueue";
|
||||
|
||||
export class VoiceChangerClient {
|
||||
private configurator: ServerConfigurator
|
||||
private ctx: AudioContext
|
||||
private vfEnable = false
|
||||
private vf: VoiceFocusDeviceTransformer | null = null
|
||||
private currentDevice: VoiceFocusTransformDevice | null = null
|
||||
private configurator: ServerConfigurator;
|
||||
private ctx: AudioContext;
|
||||
private vfEnable = false;
|
||||
private vf: VoiceFocusDeviceTransformer | null = null;
|
||||
private currentDevice: VoiceFocusTransformDevice | null = null;
|
||||
|
||||
private currentMediaStream: MediaStream | null = null
|
||||
private currentMediaStreamAudioSourceNode: MediaStreamAudioSourceNode | null = null
|
||||
private inputGainNode: GainNode | null = null
|
||||
private outputGainNode: GainNode | null = null
|
||||
private monitorGainNode: GainNode | null = null
|
||||
private vcInNode!: VoiceChangerWorkletNode
|
||||
private vcOutNode!: VoiceChangerWorkletNode
|
||||
private currentMediaStreamAudioDestinationNode!: MediaStreamAudioDestinationNode
|
||||
private currentMediaStreamAudioDestinationMonitorNode!: MediaStreamAudioDestinationNode
|
||||
private currentMediaStream: MediaStream | null = null;
|
||||
private currentMediaStreamAudioSourceNode: MediaStreamAudioSourceNode | null = null;
|
||||
private inputGainNode: GainNode | null = null;
|
||||
private outputGainNode: GainNode | null = null;
|
||||
private monitorGainNode: GainNode | null = null;
|
||||
private vcInNode!: VoiceChangerWorkletNode;
|
||||
private vcOutNode!: VoiceChangerWorkletNode;
|
||||
private currentMediaStreamAudioDestinationNode!: MediaStreamAudioDestinationNode;
|
||||
private currentMediaStreamAudioDestinationMonitorNode!: MediaStreamAudioDestinationNode;
|
||||
|
||||
private promiseForInitialize: Promise<void>;
|
||||
private _isVoiceChanging = false;
|
||||
|
||||
private promiseForInitialize: Promise<void>
|
||||
private _isVoiceChanging = false
|
||||
private setting: VoiceChangerClientSetting = DefaultClientSettng.voiceChangerClientSetting;
|
||||
|
||||
private setting: VoiceChangerClientSetting = DefaultClientSettng.voiceChangerClientSetting
|
||||
|
||||
private sslCertified: string[] = []
|
||||
private sslCertified: string[] = [];
|
||||
|
||||
private sem = new BlockingQueue<number>();
|
||||
|
||||
constructor(ctx: AudioContext, vfEnable: boolean, voiceChangerWorkletListener: VoiceChangerWorkletListener) {
|
||||
this.sem.enqueue(0);
|
||||
this.configurator = new ServerConfigurator()
|
||||
this.ctx = ctx
|
||||
this.vfEnable = vfEnable
|
||||
this.configurator = new ServerConfigurator("");
|
||||
this.ctx = ctx;
|
||||
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
|
||||
// this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
|
||||
try {
|
||||
this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
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
|
||||
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)
|
||||
const ctx44k = new AudioContext({ sampleRate: 48000 }); // 結局これが一番まし。
|
||||
// const ctx44k = new AudioContext({ sampleRate: 16000 }); // LLVCテスト⇒16K出力でプチプチなしで行ける。
|
||||
console.log("audio out:", ctx44k);
|
||||
try {
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
} catch (err) {
|
||||
await ctx44k.audioWorklet.addModule(scriptUrl)
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
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
|
||||
this.vcOutNode.connect(this.outputGainNode) // vc node -> output node
|
||||
this.outputGainNode.connect(this.currentMediaStreamAudioDestinationNode)
|
||||
this.currentMediaStreamAudioDestinationNode = ctx44k.createMediaStreamDestination(); // output node
|
||||
this.outputGainNode = ctx44k.createGain();
|
||||
this.outputGainNode.gain.value = this.setting.outputGain;
|
||||
this.vcOutNode.connect(this.outputGainNode); // vc node -> output node
|
||||
this.outputGainNode.connect(this.currentMediaStreamAudioDestinationNode);
|
||||
|
||||
this.currentMediaStreamAudioDestinationMonitorNode = ctx44k.createMediaStreamDestination() // output node
|
||||
this.monitorGainNode = ctx44k.createGain()
|
||||
this.monitorGainNode.gain.value = this.setting.monitorGain
|
||||
this.vcOutNode.connect(this.monitorGainNode) // vc node -> monitor node
|
||||
this.monitorGainNode.connect(this.currentMediaStreamAudioDestinationMonitorNode)
|
||||
this.currentMediaStreamAudioDestinationMonitorNode = ctx44k.createMediaStreamDestination(); // output node
|
||||
this.monitorGainNode = ctx44k.createGain();
|
||||
this.monitorGainNode.gain.value = this.setting.monitorGain;
|
||||
this.vcOutNode.connect(this.monitorGainNode); // vc node -> monitor node
|
||||
this.monitorGainNode.connect(this.currentMediaStreamAudioDestinationMonitorNode);
|
||||
|
||||
if (this.vfEnable) {
|
||||
this.vf = await VoiceFocusDeviceTransformer.create({ variant: 'c20' })
|
||||
const dummyMediaStream = createDummyMediaStream(this.ctx)
|
||||
this.vf = await VoiceFocusDeviceTransformer.create({ variant: "c20" });
|
||||
const dummyMediaStream = createDummyMediaStream(this.ctx);
|
||||
this.currentDevice = (await this.vf.createTransformDevice(dummyMediaStream)) || null;
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
private lock = async () => {
|
||||
@ -99,44 +97,46 @@ export class VoiceChangerClient {
|
||||
|
||||
isInitialized = async () => {
|
||||
if (this.promiseForInitialize) {
|
||||
await this.promiseForInitialize
|
||||
await this.promiseForInitialize;
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// オペレーション
|
||||
/////////////////////////////////////////////////////
|
||||
/// Operations ///
|
||||
setup = async () => {
|
||||
const lockNum = await this.lock()
|
||||
const lockNum = await this.lock();
|
||||
|
||||
console.log(`Input Setup=> echo: ${this.setting.echoCancel}, noise1: ${this.setting.noiseSuppression}, noise2: ${this.setting.noiseSuppression2}`)
|
||||
console.log(`Input Setup=> echo: ${this.setting.echoCancel}, noise1: ${this.setting.noiseSuppression}, noise2: ${this.setting.noiseSuppression2}`);
|
||||
// condition check
|
||||
if (!this.vcInNode) {
|
||||
console.warn("vc node is not initialized.")
|
||||
throw "vc node is not initialized."
|
||||
console.warn("vc node is not initialized.");
|
||||
throw "vc node is not initialized.";
|
||||
}
|
||||
|
||||
// Main Process
|
||||
//// shutdown & re-generate mediastream
|
||||
if (this.currentMediaStream) {
|
||||
this.currentMediaStream.getTracks().forEach(x => { x.stop() })
|
||||
this.currentMediaStream = null
|
||||
this.currentMediaStream.getTracks().forEach((x) => {
|
||||
x.stop();
|
||||
});
|
||||
this.currentMediaStream = null;
|
||||
}
|
||||
|
||||
//// Input デバイスがnullの時はmicStreamを止めてリターン
|
||||
if (!this.setting.audioInput) {
|
||||
console.log(`Input Setup=> client mic is disabled. ${this.setting.audioInput}`)
|
||||
this.vcInNode.stop()
|
||||
await this.unlock(lockNum)
|
||||
return
|
||||
console.log(`Input Setup=> client mic is disabled. ${this.setting.audioInput}`);
|
||||
this.vcInNode.stop();
|
||||
await this.unlock(lockNum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.setting.audioInput == "string") {
|
||||
try {
|
||||
if (this.setting.audioInput == "none") {
|
||||
this.currentMediaStream = createDummyMediaStream(this.ctx)
|
||||
this.currentMediaStream = createDummyMediaStream(this.ctx);
|
||||
} else {
|
||||
this.currentMediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
@ -146,15 +146,15 @@ export class VoiceChangerClient {
|
||||
sampleSize: 16,
|
||||
autoGainControl: false,
|
||||
echoCancellation: this.setting.echoCancel,
|
||||
noiseSuppression: this.setting.noiseSuppression
|
||||
}
|
||||
})
|
||||
noiseSuppression: this.setting.noiseSuppression,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
this.vcInNode.stop()
|
||||
await this.unlock(lockNum)
|
||||
throw e
|
||||
console.warn(e);
|
||||
this.vcInNode.stop();
|
||||
await this.unlock(lockNum);
|
||||
throw e;
|
||||
}
|
||||
// this.currentMediaStream.getAudioTracks().forEach((x) => {
|
||||
// console.log("MIC Setting(cap)", x.getCapabilities())
|
||||
@ -162,19 +162,19 @@ export class VoiceChangerClient {
|
||||
// console.log("MIC Setting(setting)", x.getSettings())
|
||||
// })
|
||||
} else {
|
||||
this.currentMediaStream = this.setting.audioInput
|
||||
this.currentMediaStream = this.setting.audioInput;
|
||||
}
|
||||
|
||||
// connect nodes.
|
||||
this.currentMediaStreamAudioSourceNode = this.ctx.createMediaStreamSource(this.currentMediaStream)
|
||||
this.inputGainNode = this.ctx.createGain()
|
||||
this.inputGainNode.gain.value = this.setting.inputGain
|
||||
this.currentMediaStreamAudioSourceNode.connect(this.inputGainNode)
|
||||
this.currentMediaStreamAudioSourceNode = this.ctx.createMediaStreamSource(this.currentMediaStream);
|
||||
this.inputGainNode = this.ctx.createGain();
|
||||
this.inputGainNode.gain.value = this.setting.inputGain;
|
||||
this.currentMediaStreamAudioSourceNode.connect(this.inputGainNode);
|
||||
if (this.currentDevice && this.setting.noiseSuppression2) {
|
||||
this.currentDevice.chooseNewInnerDevice(this.currentMediaStream)
|
||||
this.currentDevice.chooseNewInnerDevice(this.currentMediaStream);
|
||||
const voiceFocusNode = await this.currentDevice.createAudioNode(this.ctx); // vf node
|
||||
this.inputGainNode.connect(voiceFocusNode.start) // input node -> vf node
|
||||
voiceFocusNode.end.connect(this.vcInNode)
|
||||
this.inputGainNode.connect(voiceFocusNode.start); // input node -> vf node
|
||||
voiceFocusNode.end.connect(this.vcInNode);
|
||||
} else {
|
||||
// console.log("input___ media stream", this.currentMediaStream)
|
||||
// this.currentMediaStream.getTracks().forEach(x => {
|
||||
@ -184,193 +184,184 @@ export class VoiceChangerClient {
|
||||
// })
|
||||
// console.log("input___ media node", this.currentMediaStreamAudioSourceNode)
|
||||
// console.log("input___ gain node", this.inputGainNode.channelCount, this.inputGainNode)
|
||||
this.inputGainNode.connect(this.vcInNode)
|
||||
this.inputGainNode.connect(this.vcInNode);
|
||||
}
|
||||
this.vcInNode.setOutputNode(this.vcOutNode)
|
||||
console.log("Input Setup=> success")
|
||||
await this.unlock(lockNum)
|
||||
}
|
||||
this.vcInNode.setOutputNode(this.vcOutNode);
|
||||
console.log("Input Setup=> success");
|
||||
await this.unlock(lockNum);
|
||||
};
|
||||
get stream(): MediaStream {
|
||||
return this.currentMediaStreamAudioDestinationNode.stream
|
||||
return this.currentMediaStreamAudioDestinationNode.stream;
|
||||
}
|
||||
get monitorStream(): MediaStream {
|
||||
return this.currentMediaStreamAudioDestinationMonitorNode.stream
|
||||
return this.currentMediaStreamAudioDestinationMonitorNode.stream;
|
||||
}
|
||||
|
||||
start = async () => {
|
||||
await this.vcInNode.start()
|
||||
this._isVoiceChanging = true
|
||||
}
|
||||
await this.vcInNode.start();
|
||||
this._isVoiceChanging = true;
|
||||
};
|
||||
stop = async () => {
|
||||
await this.vcInNode.stop()
|
||||
this._isVoiceChanging = false
|
||||
}
|
||||
await this.vcInNode.stop();
|
||||
this._isVoiceChanging = false;
|
||||
};
|
||||
|
||||
get isVoiceChanging(): boolean {
|
||||
return this._isVoiceChanging
|
||||
return this._isVoiceChanging;
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
/// 設定
|
||||
//////////////////////////////
|
||||
setServerUrl = (serverUrl: string, openTab: boolean = false) => {
|
||||
const url = validateUrl(serverUrl)
|
||||
const pageUrl = `${location.protocol}//${location.host}`
|
||||
const url = validateUrl(serverUrl);
|
||||
const pageUrl = `${location.protocol}//${location.host}`;
|
||||
|
||||
if (url != pageUrl && url.length != 0 && location.protocol == "https:" && this.sslCertified.includes(url) == false) {
|
||||
if (openTab) {
|
||||
const value = window.confirm("MMVC Server is different from this page's origin. Open tab to open ssl connection. OK? (You can close the opened tab after ssl connection succeed.)");
|
||||
if (value) {
|
||||
window.open(url, '_blank')
|
||||
this.sslCertified.push(url)
|
||||
window.open(url, "_blank");
|
||||
this.sslCertified.push(url);
|
||||
} else {
|
||||
alert("Your voice conversion may fail...")
|
||||
alert("Your voice conversion may fail...");
|
||||
}
|
||||
}
|
||||
}
|
||||
this.vcInNode.updateSetting({ ...this.vcInNode.getSettings(), serverUrl: url })
|
||||
this.configurator.setServerUrl(url)
|
||||
}
|
||||
this.vcInNode.updateSetting({ ...this.vcInNode.getSettings(), serverUrl: url });
|
||||
this.configurator = new ServerConfigurator(url);
|
||||
};
|
||||
|
||||
updateClientSetting = async (setting: VoiceChangerClientSetting) => {
|
||||
let reconstructInputRequired = false
|
||||
if (
|
||||
this.setting.audioInput != setting.audioInput ||
|
||||
this.setting.echoCancel != setting.echoCancel ||
|
||||
this.setting.noiseSuppression != setting.noiseSuppression ||
|
||||
this.setting.noiseSuppression2 != setting.noiseSuppression2 ||
|
||||
this.setting.sampleRate != setting.sampleRate
|
||||
) {
|
||||
reconstructInputRequired = true
|
||||
let reconstructInputRequired = false;
|
||||
if (this.setting.audioInput != setting.audioInput || this.setting.echoCancel != setting.echoCancel || this.setting.noiseSuppression != setting.noiseSuppression || this.setting.noiseSuppression2 != setting.noiseSuppression2 || this.setting.sampleRate != setting.sampleRate) {
|
||||
reconstructInputRequired = true;
|
||||
}
|
||||
|
||||
if (this.setting.inputGain != setting.inputGain) {
|
||||
this.setInputGain(setting.inputGain)
|
||||
this.setInputGain(setting.inputGain);
|
||||
}
|
||||
if (this.setting.outputGain != setting.outputGain) {
|
||||
this.setOutputGain(setting.outputGain)
|
||||
this.setOutputGain(setting.outputGain);
|
||||
}
|
||||
if (this.setting.monitorGain != setting.monitorGain) {
|
||||
this.setMonitorGain(setting.monitorGain)
|
||||
this.setMonitorGain(setting.monitorGain);
|
||||
}
|
||||
|
||||
this.setting = setting
|
||||
this.setting = setting;
|
||||
if (reconstructInputRequired) {
|
||||
await this.setup()
|
||||
await this.setup();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setInputGain = (val: number) => {
|
||||
this.setting.inputGain = val
|
||||
this.setting.inputGain = val;
|
||||
if (!this.inputGainNode) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if(!val){
|
||||
return
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
this.inputGainNode.gain.value = val
|
||||
}
|
||||
this.inputGainNode.gain.value = val;
|
||||
};
|
||||
|
||||
setOutputGain = (val: number) => {
|
||||
if (!this.outputGainNode) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if(!val){
|
||||
return
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
this.outputGainNode.gain.value = val
|
||||
}
|
||||
this.outputGainNode.gain.value = val;
|
||||
};
|
||||
|
||||
setMonitorGain = (val: number) => {
|
||||
if (!this.monitorGainNode) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if(!val){
|
||||
return
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
this.monitorGainNode.gain.value = val
|
||||
}
|
||||
this.monitorGainNode.gain.value = val;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// コンポーネント設定、操作
|
||||
/////////////////////////////////////////////////////
|
||||
//## Server ##//
|
||||
getModelType = () => {
|
||||
return this.configurator.getModelType()
|
||||
}
|
||||
return this.configurator.getModelType();
|
||||
};
|
||||
getOnnx = async () => {
|
||||
return this.configurator.export2onnx()
|
||||
}
|
||||
return this.configurator.export2onnx();
|
||||
};
|
||||
mergeModel = async (req: MergeModelRequest) => {
|
||||
return this.configurator.mergeModel(req)
|
||||
}
|
||||
return this.configurator.mergeModel(req);
|
||||
};
|
||||
updateModelDefault = async () => {
|
||||
return this.configurator.updateModelDefault()
|
||||
}
|
||||
return this.configurator.updateModelDefault();
|
||||
};
|
||||
updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
return this.configurator.updateModelInfo(slot, key, val)
|
||||
}
|
||||
return this.configurator.updateModelInfo(slot, key, val);
|
||||
};
|
||||
|
||||
updateServerSettings = (key: ServerSettingKey, val: string) => {
|
||||
return this.configurator.updateSettings(key, val)
|
||||
}
|
||||
return this.configurator.updateSettings(key, val);
|
||||
};
|
||||
uploadFile = (buf: ArrayBuffer, filename: string, onprogress: (progress: number, end: boolean) => void) => {
|
||||
return this.configurator.uploadFile(buf, filename, onprogress)
|
||||
}
|
||||
return this.configurator.uploadFile(buf, filename, onprogress);
|
||||
};
|
||||
uploadFile2 = (dir: string, file: File, onprogress: (progress: number, end: boolean) => void) => {
|
||||
return this.configurator.uploadFile2(dir, file, onprogress)
|
||||
}
|
||||
return this.configurator.uploadFile2(dir, file, onprogress);
|
||||
};
|
||||
concatUploadedFile = (filename: string, chunkNum: number) => {
|
||||
return this.configurator.concatUploadedFile(filename, chunkNum)
|
||||
}
|
||||
loadModel = (
|
||||
slot: number,
|
||||
isHalf: boolean,
|
||||
params: string,
|
||||
) => {
|
||||
return this.configurator.loadModel(slot, isHalf, params)
|
||||
}
|
||||
return this.configurator.concatUploadedFile(filename, chunkNum);
|
||||
};
|
||||
loadModel = (slot: number, isHalf: boolean, params: string) => {
|
||||
return this.configurator.loadModel(slot, isHalf, params);
|
||||
};
|
||||
uploadAssets = (params: string) => {
|
||||
return this.configurator.uploadAssets(params)
|
||||
}
|
||||
return this.configurator.uploadAssets(params);
|
||||
};
|
||||
|
||||
//## Worklet ##//
|
||||
configureWorklet = (setting: WorkletSetting) => {
|
||||
this.vcInNode.configure(setting)
|
||||
this.vcOutNode.configure(setting)
|
||||
}
|
||||
this.vcInNode.configure(setting);
|
||||
this.vcOutNode.configure(setting);
|
||||
};
|
||||
startOutputRecording = () => {
|
||||
this.vcOutNode.startOutputRecording()
|
||||
}
|
||||
this.vcOutNode.startOutputRecording();
|
||||
};
|
||||
stopOutputRecording = () => {
|
||||
return this.vcOutNode.stopOutputRecording()
|
||||
}
|
||||
return this.vcOutNode.stopOutputRecording();
|
||||
};
|
||||
trancateBuffer = () => {
|
||||
this.vcOutNode.trancateBuffer()
|
||||
}
|
||||
this.vcOutNode.trancateBuffer();
|
||||
};
|
||||
//## Worklet Node ##//
|
||||
updateWorkletNodeSetting = (setting: WorkletNodeSetting) => {
|
||||
this.vcInNode.updateSetting(setting)
|
||||
this.vcOutNode.updateSetting(setting)
|
||||
}
|
||||
|
||||
this.vcInNode.updateSetting(setting);
|
||||
this.vcOutNode.updateSetting(setting);
|
||||
};
|
||||
setInternalAudioProcessCallback = (internalCallback: InternalCallback) => {
|
||||
this.vcInNode.setInternalAudioProcessCallback(internalCallback);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// 情報取得
|
||||
/////////////////////////////////////////////////////
|
||||
// Information
|
||||
getClientSettings = () => {
|
||||
return this.vcInNode.getSettings()
|
||||
}
|
||||
return this.vcInNode.getSettings();
|
||||
};
|
||||
getServerSettings = () => {
|
||||
return this.configurator.getSettings()
|
||||
}
|
||||
return this.configurator.getSettings();
|
||||
};
|
||||
getPerformance = () => {
|
||||
return this.configurator.getPerformance()
|
||||
}
|
||||
return this.configurator.getPerformance();
|
||||
};
|
||||
|
||||
getSocketId = () => {
|
||||
return this.vcInNode.getSocketId()
|
||||
}
|
||||
|
||||
}
|
||||
return this.vcInNode.getSocketId();
|
||||
};
|
||||
}
|
||||
|
@ -1,402 +0,0 @@
|
||||
import { VoiceChangerWorkletProcessorRequest } from "./@types/voice-changer-worklet-processor";
|
||||
import { DefaultClientSettng, DownSamplingMode, VOICE_CHANGER_CLIENT_EXCEPTION, WorkletNodeSetting, WorkletSetting } from "./const";
|
||||
import { io, Socket } from "socket.io-client";
|
||||
import { DefaultEventsMap } from "@socket.io/component-emitter";
|
||||
|
||||
export type VoiceChangerWorkletListener = {
|
||||
notifyVolume: (vol: number) => void
|
||||
notifySendBufferingTime: (time: number) => void
|
||||
notifyResponseTime: (time: number, perf?: number[]) => void
|
||||
notifyException: (code: VOICE_CHANGER_CLIENT_EXCEPTION, message: string) => void
|
||||
}
|
||||
|
||||
export class VoiceChangerWorkletNode extends AudioWorkletNode {
|
||||
private listener: VoiceChangerWorkletListener
|
||||
|
||||
private setting: WorkletNodeSetting = DefaultClientSettng.workletNodeSetting
|
||||
private requestChunks: ArrayBuffer[] = []
|
||||
private socket: Socket<DefaultEventsMap, DefaultEventsMap> | null = null
|
||||
// performance monitor
|
||||
private bufferStart = 0;
|
||||
|
||||
private isOutputRecording = false;
|
||||
private recordingOutputChunk: Float32Array[] = []
|
||||
private outputNode: VoiceChangerWorkletNode | null = null
|
||||
|
||||
// Promises
|
||||
private startPromiseResolve: ((value: void | PromiseLike<void>) => void) | null = null
|
||||
private stopPromiseResolve: ((value: void | PromiseLike<void>) => void) | null = null
|
||||
|
||||
|
||||
|
||||
constructor(context: AudioContext, listener: VoiceChangerWorkletListener) {
|
||||
super(context, "voice-changer-worklet-processor");
|
||||
this.port.onmessage = this.handleMessage.bind(this);
|
||||
this.listener = listener
|
||||
this.createSocketIO()
|
||||
console.log(`[worklet_node][voice-changer-worklet-processor] created.`);
|
||||
}
|
||||
|
||||
setOutputNode = (outputNode: VoiceChangerWorkletNode | null) => {
|
||||
this.outputNode = outputNode
|
||||
}
|
||||
|
||||
|
||||
// 設定
|
||||
updateSetting = (setting: WorkletNodeSetting) => {
|
||||
console.log(`[WorkletNode] Updating WorkletNode Setting,`, this.setting, setting)
|
||||
let recreateSocketIoRequired = false
|
||||
if (this.setting.serverUrl != setting.serverUrl || this.setting.protocol != setting.protocol) {
|
||||
recreateSocketIoRequired = true
|
||||
}
|
||||
this.setting = setting
|
||||
if (recreateSocketIoRequired) {
|
||||
this.createSocketIO()
|
||||
}
|
||||
}
|
||||
|
||||
getSettings = (): WorkletNodeSetting => {
|
||||
return this.setting
|
||||
}
|
||||
|
||||
getSocketId = () => {
|
||||
return this.socket?.id
|
||||
}
|
||||
|
||||
// 処理
|
||||
private createSocketIO = () => {
|
||||
if (this.socket) {
|
||||
this.socket.close()
|
||||
}
|
||||
if (this.setting.protocol === "sio") {
|
||||
this.socket = io(this.setting.serverUrl + "/test");
|
||||
this.socket.on('connect_error', (err) => {
|
||||
this.listener.notifyException(VOICE_CHANGER_CLIENT_EXCEPTION.ERR_SIO_CONNECT_FAILED, `[SIO] rconnection failed ${err}`)
|
||||
})
|
||||
this.socket.on('connect', () => {
|
||||
console.log(`[SIO] connect to ${this.setting.serverUrl}`)
|
||||
console.log(`[SIO] ${this.socket?.id}`)
|
||||
});
|
||||
this.socket.on('close', function (socket) {
|
||||
console.log(`[SIO] close ${socket.id}`)
|
||||
});
|
||||
|
||||
|
||||
this.socket.on('message', (response: any[]) => {
|
||||
console.log("message:", response)
|
||||
});
|
||||
|
||||
this.socket.on('response', (response: any[]) => {
|
||||
|
||||
const cur = Date.now()
|
||||
const responseTime = cur - response[0]
|
||||
const result = response[1] as ArrayBuffer
|
||||
const perf = response[2]
|
||||
|
||||
// Quick hack for server device mode
|
||||
if (response[0] == 0) {
|
||||
this.listener.notifyResponseTime(Math.round(perf[0] * 1000), perf.slice(1, 4))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (result.byteLength < 128 * 2) {
|
||||
this.listener.notifyException(VOICE_CHANGER_CLIENT_EXCEPTION.ERR_SIO_INVALID_RESPONSE, `[SIO] recevied data is too short ${result.byteLength}`)
|
||||
} else {
|
||||
if (this.outputNode != null) {
|
||||
this.outputNode.postReceivedVoice(response[1])
|
||||
} else {
|
||||
this.postReceivedVoice(response[1])
|
||||
}
|
||||
this.listener.notifyResponseTime(responseTime, perf)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
postReceivedVoice = (data: ArrayBuffer) => {
|
||||
// Int16 to Float
|
||||
const i16Data = new Int16Array(data)
|
||||
const f32Data = new Float32Array(i16Data.length)
|
||||
// console.log(`[worklet] f32DataLength${f32Data.length} i16DataLength${i16Data.length}`)
|
||||
i16Data.forEach((x, i) => {
|
||||
const float = (x >= 0x8000) ? -(0x10000 - x) / 0x8000 : x / 0x7FFF;
|
||||
f32Data[i] = float
|
||||
})
|
||||
|
||||
// アップサンプリング
|
||||
let upSampledBuffer: Float32Array | null = null
|
||||
if (this.setting.sendingSampleRate == 48000) {
|
||||
upSampledBuffer = f32Data
|
||||
} else {
|
||||
upSampledBuffer = new Float32Array(f32Data.length * 2)
|
||||
for (let i = 0; i < f32Data.length; i++) {
|
||||
const currentFrame = f32Data[i]
|
||||
const nextFrame = i + 1 < f32Data.length ? f32Data[i + 1] : f32Data[i]
|
||||
upSampledBuffer[i * 2] = currentFrame
|
||||
upSampledBuffer[i * 2 + 1] = (currentFrame + nextFrame) / 2
|
||||
}
|
||||
}
|
||||
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "voice",
|
||||
voice: upSampledBuffer,
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0
|
||||
}
|
||||
this.port.postMessage(req)
|
||||
|
||||
if (this.isOutputRecording) {
|
||||
this.recordingOutputChunk.push(upSampledBuffer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private _averageDownsampleBuffer(buffer: Float32Array, originalSampleRate: number, destinationSamplerate: number) {
|
||||
if (originalSampleRate == destinationSamplerate) {
|
||||
return buffer;
|
||||
}
|
||||
if (destinationSamplerate > originalSampleRate) {
|
||||
throw "downsampling rate show be smaller than original sample rate";
|
||||
}
|
||||
const sampleRateRatio = originalSampleRate / destinationSamplerate;
|
||||
const newLength = Math.round(buffer.length / sampleRateRatio);
|
||||
const result = new Float32Array(newLength);
|
||||
let offsetResult = 0;
|
||||
let offsetBuffer = 0;
|
||||
while (offsetResult < result.length) {
|
||||
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
|
||||
// Use average value of skipped samples
|
||||
var accum = 0, count = 0;
|
||||
for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
|
||||
accum += buffer[i];
|
||||
count++;
|
||||
}
|
||||
result[offsetResult] = accum / count;
|
||||
// Or you can simply get rid of the skipped samples:
|
||||
// result[offsetResult] = buffer[nextOffsetBuffer];
|
||||
offsetResult++;
|
||||
offsetBuffer = nextOffsetBuffer;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
handleMessage(event: any) {
|
||||
// console.log(`[Node:handleMessage_] `, event.data.volume);
|
||||
if (event.data.responseType === "start_ok") {
|
||||
if (this.startPromiseResolve) {
|
||||
this.startPromiseResolve()
|
||||
this.startPromiseResolve = null
|
||||
}
|
||||
} else if (event.data.responseType === "stop_ok") {
|
||||
if (this.stopPromiseResolve) {
|
||||
this.stopPromiseResolve()
|
||||
this.stopPromiseResolve = null
|
||||
}
|
||||
} else if (event.data.responseType === "volume") {
|
||||
this.listener.notifyVolume(event.data.volume as number)
|
||||
} else if (event.data.responseType === "inputData") {
|
||||
const inputData = event.data.inputData as Float32Array
|
||||
// console.log("receive input data", inputData)
|
||||
|
||||
// ダウンサンプリング
|
||||
let downsampledBuffer: Float32Array | null = null
|
||||
if (this.setting.sendingSampleRate == 48000) {
|
||||
downsampledBuffer = inputData
|
||||
} else if (this.setting.downSamplingMode == DownSamplingMode.decimate) {
|
||||
//////// (Kind 1) 間引き //////////
|
||||
//// 48000Hz で入ってくるので間引いて24000Hzに変換する。
|
||||
downsampledBuffer = new Float32Array(inputData.length / 2);
|
||||
for (let i = 0; i < inputData.length; i++) {
|
||||
if (i % 2 == 0) {
|
||||
downsampledBuffer[i / 2] = inputData[i]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//////// (Kind 2) 平均 //////////
|
||||
// downsampledBuffer = this._averageDownsampleBuffer(buffer, 48000, 24000)
|
||||
downsampledBuffer = this._averageDownsampleBuffer(inputData, 48000, this.setting.sendingSampleRate)
|
||||
}
|
||||
|
||||
// Float to Int16
|
||||
const arrayBuffer = new ArrayBuffer(downsampledBuffer.length * 2)
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
for (let i = 0; i < downsampledBuffer.length; i++) {
|
||||
let s = Math.max(-1, Math.min(1, downsampledBuffer[i]));
|
||||
s = s < 0 ? s * 0x8000 : s * 0x7FFF
|
||||
dataView.setInt16(i * 2, s, true);
|
||||
}
|
||||
|
||||
// バッファリング
|
||||
this.requestChunks.push(arrayBuffer)
|
||||
|
||||
//// リクエストバッファの中身が、リクエスト送信数と違う場合は処理終了。
|
||||
if (this.requestChunks.length < this.setting.inputChunkNum) {
|
||||
return
|
||||
}
|
||||
|
||||
// リクエスト用の入れ物を作成
|
||||
const windowByteLength = this.requestChunks.reduce((prev, cur) => {
|
||||
return prev + cur.byteLength
|
||||
}, 0)
|
||||
const newBuffer = new Uint8Array(windowByteLength);
|
||||
|
||||
// リクエストのデータをセット
|
||||
this.requestChunks.reduce((prev, cur) => {
|
||||
newBuffer.set(new Uint8Array(cur), prev)
|
||||
return prev + cur.byteLength
|
||||
}, 0)
|
||||
|
||||
|
||||
this.sendBuffer(newBuffer)
|
||||
this.requestChunks = []
|
||||
|
||||
this.listener.notifySendBufferingTime(Date.now() - this.bufferStart)
|
||||
this.bufferStart = Date.now()
|
||||
|
||||
} else {
|
||||
console.warn(`[worklet_node][voice-changer-worklet-processor] unknown response ${event.data.responseType}`, event.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private sendBuffer = async (newBuffer: Uint8Array) => {
|
||||
const timestamp = Date.now()
|
||||
if (this.setting.protocol === "sio") {
|
||||
if (!this.socket) {
|
||||
console.warn(`sio is not initialized`)
|
||||
return
|
||||
}
|
||||
// console.log("emit!")
|
||||
this.socket.emit('request_message', [
|
||||
timestamp,
|
||||
newBuffer.buffer]);
|
||||
} else {
|
||||
const res = await postVoice(
|
||||
this.setting.serverUrl + "/test",
|
||||
timestamp,
|
||||
newBuffer.buffer)
|
||||
|
||||
if (res.byteLength < 128 * 2) {
|
||||
this.listener.notifyException(VOICE_CHANGER_CLIENT_EXCEPTION.ERR_REST_INVALID_RESPONSE, `[REST] recevied data is too short ${res.byteLength}`)
|
||||
} else {
|
||||
if (this.outputNode != null) {
|
||||
this.outputNode.postReceivedVoice(res)
|
||||
} else {
|
||||
this.postReceivedVoice(res)
|
||||
}
|
||||
this.listener.notifyResponseTime(Date.now() - timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
configure = (setting: WorkletSetting) => {
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "config",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: setting.numTrancateTreshold,
|
||||
volTrancateThreshold: setting.volTrancateThreshold,
|
||||
volTrancateLength: setting.volTrancateLength
|
||||
}
|
||||
this.port.postMessage(req)
|
||||
}
|
||||
|
||||
start = async () => {
|
||||
const p = new Promise<void>((resolve) => {
|
||||
this.startPromiseResolve = resolve
|
||||
})
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "start",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0
|
||||
}
|
||||
this.port.postMessage(req)
|
||||
await p
|
||||
|
||||
}
|
||||
stop = async () => {
|
||||
const p = new Promise<void>((resolve) => {
|
||||
this.stopPromiseResolve = resolve
|
||||
})
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "stop",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0
|
||||
}
|
||||
this.port.postMessage(req)
|
||||
await p
|
||||
}
|
||||
trancateBuffer = () => {
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "trancateBuffer",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0
|
||||
}
|
||||
this.port.postMessage(req)
|
||||
}
|
||||
|
||||
startOutputRecording = () => {
|
||||
this.recordingOutputChunk = []
|
||||
this.isOutputRecording = true
|
||||
}
|
||||
stopOutputRecording = () => {
|
||||
this.isOutputRecording = false
|
||||
|
||||
const dataSize = this.recordingOutputChunk.reduce((prev, cur) => {
|
||||
return prev + cur.length
|
||||
}, 0)
|
||||
const samples = new Float32Array(dataSize);
|
||||
let sampleIndex = 0
|
||||
for (let i = 0; i < this.recordingOutputChunk.length; i++) {
|
||||
for (let j = 0; j < this.recordingOutputChunk[i].length; j++) {
|
||||
samples[sampleIndex] = this.recordingOutputChunk[i][j];
|
||||
sampleIndex++;
|
||||
}
|
||||
}
|
||||
return samples
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const postVoice = async (
|
||||
url: string,
|
||||
timestamp: number,
|
||||
buffer: ArrayBuffer) => {
|
||||
const obj = {
|
||||
timestamp,
|
||||
buffer: Buffer.from(buffer).toString('base64')
|
||||
};
|
||||
const body = JSON.stringify(obj);
|
||||
|
||||
const res = await fetch(`${url}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: body
|
||||
})
|
||||
|
||||
try {
|
||||
const receivedJson = await res.json()
|
||||
const changedVoiceBase64 = receivedJson["changedVoiceBase64"]
|
||||
const buf = Buffer.from(changedVoiceBase64, "base64")
|
||||
const ab = new ArrayBuffer(buf.length);
|
||||
const view = new Uint8Array(ab);
|
||||
for (let i = 0; i < buf.length; ++i) {
|
||||
view[i] = buf[i];
|
||||
}
|
||||
return ab
|
||||
} catch (e) {
|
||||
console.log("Exception:", e)
|
||||
return new ArrayBuffer(10);
|
||||
}
|
||||
}
|
62
client/lib/src/client/ServerConfigurator.ts
Normal file
62
client/lib/src/client/ServerConfigurator.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { MergeModelRequest, ServerSettingKey } from "../const";
|
||||
import { ServerRestClient } from "./ServerRestClient";
|
||||
|
||||
export class ServerConfigurator {
|
||||
private restClient;
|
||||
|
||||
constructor(serverUrl: string) {
|
||||
this.restClient = new ServerRestClient(serverUrl);
|
||||
}
|
||||
|
||||
getSettings = async () => {
|
||||
return this.restClient.getSettings();
|
||||
};
|
||||
|
||||
getPerformance = async () => {
|
||||
return this.restClient.getPerformance();
|
||||
};
|
||||
|
||||
updateSettings = async (key: ServerSettingKey, val: string) => {
|
||||
return this.restClient.updateSettings(key, val);
|
||||
};
|
||||
|
||||
uploadFile2 = async (dir: string, file: File, onprogress: (progress: number, end: boolean) => void) => {
|
||||
return this.restClient.uploadFile2(dir, file, onprogress);
|
||||
};
|
||||
|
||||
uploadFile = async (buf: ArrayBuffer, filename: string, onprogress: (progress: number, end: boolean) => void) => {
|
||||
return this.restClient.uploadFile(buf, filename, onprogress);
|
||||
};
|
||||
|
||||
concatUploadedFile = async (filename: string, chunkNum: number) => {
|
||||
return this.restClient.concatUploadedFile(filename, chunkNum);
|
||||
};
|
||||
|
||||
loadModel = async (slot: number, isHalf: boolean, params: string = "{}") => {
|
||||
return this.restClient.loadModel(slot, isHalf, params);
|
||||
};
|
||||
|
||||
uploadAssets = async (params: string) => {
|
||||
return this.restClient.uploadAssets(params);
|
||||
};
|
||||
|
||||
getModelType = async () => {
|
||||
return this.restClient.getModelType();
|
||||
};
|
||||
|
||||
export2onnx = async () => {
|
||||
return this.restClient.export2onnx();
|
||||
};
|
||||
|
||||
mergeModel = async (req: MergeModelRequest) => {
|
||||
return this.restClient.mergeModel(req);
|
||||
};
|
||||
|
||||
updateModelDefault = async () => {
|
||||
return this.restClient.updateModelDefault();
|
||||
};
|
||||
|
||||
updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
return this.restClient.updateModelInfo(slot, key, val);
|
||||
};
|
||||
}
|
@ -1,108 +1,107 @@
|
||||
import { MergeModelRequest, OnnxExporterInfo, ServerInfo, ServerSettingKey } from "./const";
|
||||
|
||||
import { MergeModelRequest, OnnxExporterInfo, ServerInfo, ServerSettingKey } from "../const";
|
||||
|
||||
type FileChunk = {
|
||||
hash: number,
|
||||
chunk: ArrayBuffer
|
||||
}
|
||||
export class ServerConfigurator {
|
||||
private serverUrl = ""
|
||||
hash: number;
|
||||
chunk: ArrayBuffer;
|
||||
};
|
||||
|
||||
setServerUrl = (serverUrl: string) => {
|
||||
this.serverUrl = serverUrl
|
||||
console.log(`[ServerConfigurator] Server URL: ${this.serverUrl}`)
|
||||
export class ServerRestClient {
|
||||
private serverUrl = "";
|
||||
|
||||
constructor(serverUrl: string) {
|
||||
this.serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
getSettings = async () => {
|
||||
const url = this.serverUrl + "/info"
|
||||
const url = this.serverUrl + "/info";
|
||||
const info = await new Promise<ServerInfo>((resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
});
|
||||
fetch(request).then(async (response) => {
|
||||
const json = await response.json() as ServerInfo
|
||||
resolve(json)
|
||||
})
|
||||
})
|
||||
return info
|
||||
}
|
||||
const json = (await response.json()) as ServerInfo;
|
||||
resolve(json);
|
||||
});
|
||||
});
|
||||
return info;
|
||||
};
|
||||
|
||||
getPerformance = async () => {
|
||||
const url = this.serverUrl + "/performance"
|
||||
const url = this.serverUrl + "/performance";
|
||||
const info = await new Promise<number[]>((resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
});
|
||||
fetch(request).then(async (response) => {
|
||||
const json = await response.json() as number[]
|
||||
resolve(json)
|
||||
})
|
||||
})
|
||||
return info
|
||||
}
|
||||
const json = (await response.json()) as number[];
|
||||
resolve(json);
|
||||
});
|
||||
});
|
||||
return info;
|
||||
};
|
||||
|
||||
updateSettings = async (key: ServerSettingKey, val: string) => {
|
||||
const url = this.serverUrl + "/update_settings"
|
||||
const url = this.serverUrl + "/update_settings";
|
||||
const info = await new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("key", key);
|
||||
formData.append("val", val);
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
resolve(res);
|
||||
});
|
||||
return info;
|
||||
};
|
||||
|
||||
uploadFile2 = async (dir: string, file: File, onprogress: (progress: number, end: boolean) => void) => {
|
||||
const url = this.serverUrl + "/upload_file"
|
||||
onprogress(0, false)
|
||||
const url = this.serverUrl + "/upload_file";
|
||||
onprogress(0, false);
|
||||
const size = 1024 * 1024;
|
||||
let index = 0; // index値
|
||||
const fileLength = file.size
|
||||
const filename = dir + file.name
|
||||
const fileChunkNum = Math.ceil(fileLength / size)
|
||||
const fileLength = file.size;
|
||||
const filename = dir + file.name;
|
||||
const fileChunkNum = Math.ceil(fileLength / size);
|
||||
|
||||
while (true) {
|
||||
const promises: Promise<void>[] = []
|
||||
const promises: Promise<void>[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (index * size >= fileLength) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
const chunk = file.slice(index * size, (index + 1) * size)
|
||||
const chunk = file.slice(index * size, (index + 1) * size);
|
||||
|
||||
const p = new Promise<void>((resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", new Blob([chunk]));
|
||||
formData.append("filename", `${filename}_${index}`);
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
fetch(request).then(async (_response) => {
|
||||
// console.log(await response.text())
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
index += 1
|
||||
promises.push(p)
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
index += 1;
|
||||
promises.push(p);
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
await Promise.all(promises);
|
||||
if (index * size >= fileLength) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
onprogress(Math.floor(((index) / (fileChunkNum + 1)) * 100), false)
|
||||
onprogress(Math.floor((index / (fileChunkNum + 1)) * 100), false);
|
||||
}
|
||||
return fileChunkNum
|
||||
}
|
||||
return fileChunkNum;
|
||||
};
|
||||
|
||||
uploadFile = async (buf: ArrayBuffer, filename: string, onprogress: (progress: number, end: boolean) => void) => {
|
||||
const url = this.serverUrl + "/upload_file"
|
||||
onprogress(0, false)
|
||||
const url = this.serverUrl + "/upload_file";
|
||||
onprogress(0, false);
|
||||
const size = 1024 * 1024;
|
||||
const fileChunks: FileChunk[] = [];
|
||||
let index = 0; // index値
|
||||
@ -113,65 +112,64 @@ export class ServerConfigurator {
|
||||
});
|
||||
}
|
||||
|
||||
const chunkNum = fileChunks.length
|
||||
const chunkNum = fileChunks.length;
|
||||
// console.log("FILE_CHUNKS:", chunkNum, fileChunks)
|
||||
|
||||
|
||||
while (true) {
|
||||
const promises: Promise<void>[] = []
|
||||
const promises: Promise<void>[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const chunk = fileChunks.shift()
|
||||
const chunk = fileChunks.shift();
|
||||
if (!chunk) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
const p = new Promise<void>((resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", new Blob([chunk.chunk]));
|
||||
formData.append("filename", `${filename}_${chunk.hash}`);
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
fetch(request).then(async (_response) => {
|
||||
// console.log(await response.text())
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
promises.push(p)
|
||||
promises.push(p);
|
||||
}
|
||||
await Promise.all(promises)
|
||||
await Promise.all(promises);
|
||||
if (fileChunks.length == 0) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
onprogress(Math.floor(((chunkNum - fileChunks.length) / (chunkNum + 1)) * 100), false)
|
||||
onprogress(Math.floor(((chunkNum - fileChunks.length) / (chunkNum + 1)) * 100), false);
|
||||
}
|
||||
return chunkNum
|
||||
}
|
||||
return chunkNum;
|
||||
};
|
||||
|
||||
concatUploadedFile = async (filename: string, chunkNum: number) => {
|
||||
const url = this.serverUrl + "/concat_uploaded_file"
|
||||
const url = this.serverUrl + "/concat_uploaded_file";
|
||||
await new Promise<void>((resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("filename", filename);
|
||||
formData.append("filenameChunkNum", "" + chunkNum);
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
fetch(request).then(async (response) => {
|
||||
console.log(await response.text())
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
console.log(await response.text());
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
loadModel = async (slot: number, isHalf: boolean, params: string = "{}") => {
|
||||
if (isHalf == undefined || isHalf == null) {
|
||||
console.warn("isHalf is invalid value", isHalf)
|
||||
isHalf = false
|
||||
console.warn("isHalf is invalid value", isHalf);
|
||||
isHalf = false;
|
||||
}
|
||||
const url = this.serverUrl + "/load_model"
|
||||
const url = this.serverUrl + "/load_model";
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("slot", "" + slot);
|
||||
@ -179,102 +177,137 @@ export class ServerConfigurator {
|
||||
formData.append("params", params);
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
uploadAssets = async (params: string) => {
|
||||
const url = this.serverUrl + "/upload_model_assets"
|
||||
const url = this.serverUrl + "/upload_model_assets";
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("params", params);
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
getModelType = async () => {
|
||||
const url = this.serverUrl + "/model_type"
|
||||
const url = this.serverUrl + "/model_type";
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
export2onnx = async () => {
|
||||
const url = this.serverUrl + "/onnx"
|
||||
const url = this.serverUrl + "/onnx";
|
||||
const info = new Promise<OnnxExporterInfo>(async (resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
});
|
||||
const res = await (await fetch(request)).json() as OnnxExporterInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as OnnxExporterInfo;
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
mergeModel = async (req: MergeModelRequest) => {
|
||||
const url = this.serverUrl + "/merge_model"
|
||||
const url = this.serverUrl + "/merge_model";
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("request", JSON.stringify(req));
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
console.log("RESPONSE", res)
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
console.log("RESPONSE", res);
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
updateModelDefault = async () => {
|
||||
const url = this.serverUrl + "/update_model_default"
|
||||
const url = this.serverUrl + "/update_model_default";
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
console.log("RESPONSE", res)
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
console.log("RESPONSE", res);
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
const url = this.serverUrl + "/update_model_info"
|
||||
const newData = { slot, key, val }
|
||||
const url = this.serverUrl + "/update_model_info";
|
||||
const newData = { slot, key, val };
|
||||
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("newData", JSON.stringify(newData));
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
console.log("RESPONSE", res)
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
const res = (await (await fetch(request)).json()) as ServerInfo;
|
||||
console.log("RESPONSE", res);
|
||||
resolve(res);
|
||||
});
|
||||
return await info;
|
||||
};
|
||||
|
||||
// VoiceChangerWorkletNodeから呼び出される
|
||||
//// Restで音声変換
|
||||
postVoice = async (timestamp: number, buffer: ArrayBuffer) => {
|
||||
const url = this.serverUrl + "/test";
|
||||
|
||||
const obj = {
|
||||
timestamp,
|
||||
buffer: Buffer.from(buffer).toString("base64"),
|
||||
};
|
||||
const body = JSON.stringify(obj);
|
||||
|
||||
const res = await fetch(`${url}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: body,
|
||||
});
|
||||
|
||||
try {
|
||||
const receivedJson = await res.json();
|
||||
const changedVoiceBase64 = receivedJson["changedVoiceBase64"];
|
||||
const buf = Buffer.from(changedVoiceBase64, "base64");
|
||||
const ab = new ArrayBuffer(buf.length);
|
||||
const view = new Uint8Array(ab);
|
||||
for (let i = 0; i < buf.length; ++i) {
|
||||
view[i] = buf[i];
|
||||
}
|
||||
return ab;
|
||||
} catch (e) {
|
||||
console.log("Exception:", e);
|
||||
return new ArrayBuffer(10);
|
||||
}
|
||||
};
|
||||
}
|
9
client/lib/src/client/VoiceChangerWorkletNode.test.ts
Normal file
9
client/lib/src/client/VoiceChangerWorkletNode.test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
describe("test1", () => {
|
||||
test("test222", () => {
|
||||
expect(
|
||||
(() => {
|
||||
return 1;
|
||||
})()
|
||||
).toBe(1);
|
||||
});
|
||||
});
|
443
client/lib/src/client/VoiceChangerWorkletNode.ts
Normal file
443
client/lib/src/client/VoiceChangerWorkletNode.ts
Normal file
@ -0,0 +1,443 @@
|
||||
import { VoiceChangerWorkletProcessorRequest } from "../@types/voice-changer-worklet-processor";
|
||||
import {
|
||||
DefaultClientSettng,
|
||||
DownSamplingMode,
|
||||
VOICE_CHANGER_CLIENT_EXCEPTION,
|
||||
WorkletNodeSetting,
|
||||
WorkletSetting,
|
||||
} from "../const";
|
||||
import { io, Socket } from "socket.io-client";
|
||||
import { DefaultEventsMap } from "@socket.io/component-emitter";
|
||||
import { ServerRestClient } from "./ServerRestClient";
|
||||
|
||||
export type VoiceChangerWorkletListener = {
|
||||
notifyVolume: (vol: number) => void;
|
||||
notifySendBufferingTime: (time: number) => void;
|
||||
notifyResponseTime: (time: number, perf?: number[]) => void;
|
||||
notifyException: (
|
||||
code: VOICE_CHANGER_CLIENT_EXCEPTION,
|
||||
message: string
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type InternalCallback = {
|
||||
processAudio: (data: Uint8Array) => Promise<Uint8Array>;
|
||||
};
|
||||
|
||||
export class VoiceChangerWorkletNode extends AudioWorkletNode {
|
||||
private listener: VoiceChangerWorkletListener;
|
||||
|
||||
private setting: WorkletNodeSetting = DefaultClientSettng.workletNodeSetting;
|
||||
private requestChunks: ArrayBuffer[] = [];
|
||||
private socket: Socket<DefaultEventsMap, DefaultEventsMap> | null = null;
|
||||
// performance monitor
|
||||
private bufferStart = 0;
|
||||
|
||||
private isOutputRecording = false;
|
||||
private recordingOutputChunk: Float32Array[] = [];
|
||||
private outputNode: VoiceChangerWorkletNode | null = null;
|
||||
|
||||
// Promises
|
||||
private startPromiseResolve:
|
||||
| ((value: void | PromiseLike<void>) => void)
|
||||
| null = null;
|
||||
private stopPromiseResolve:
|
||||
| ((value: void | PromiseLike<void>) => void)
|
||||
| null = null;
|
||||
|
||||
// InternalCallback
|
||||
private internalCallback: InternalCallback | null = null;
|
||||
|
||||
constructor(context: AudioContext, listener: VoiceChangerWorkletListener) {
|
||||
super(context, "voice-changer-worklet-processor");
|
||||
this.port.onmessage = this.handleMessage.bind(this);
|
||||
this.listener = listener;
|
||||
this.createSocketIO();
|
||||
console.log(`[worklet_node][voice-changer-worklet-processor] created.`);
|
||||
}
|
||||
|
||||
setOutputNode = (outputNode: VoiceChangerWorkletNode | null) => {
|
||||
this.outputNode = outputNode;
|
||||
};
|
||||
|
||||
// 設定
|
||||
updateSetting = (setting: WorkletNodeSetting) => {
|
||||
console.log(
|
||||
`[WorkletNode] Updating WorkletNode Setting,`,
|
||||
this.setting,
|
||||
setting
|
||||
);
|
||||
let recreateSocketIoRequired = false;
|
||||
if (
|
||||
this.setting.serverUrl != setting.serverUrl ||
|
||||
this.setting.protocol != setting.protocol
|
||||
) {
|
||||
recreateSocketIoRequired = true;
|
||||
}
|
||||
this.setting = setting;
|
||||
if (recreateSocketIoRequired) {
|
||||
this.createSocketIO();
|
||||
}
|
||||
};
|
||||
|
||||
setInternalAudioProcessCallback = (internalCallback: InternalCallback) => {
|
||||
this.internalCallback = internalCallback;
|
||||
};
|
||||
|
||||
getSettings = (): WorkletNodeSetting => {
|
||||
return this.setting;
|
||||
};
|
||||
|
||||
getSocketId = () => {
|
||||
return this.socket?.id;
|
||||
};
|
||||
|
||||
// 処理
|
||||
private createSocketIO = () => {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
if (this.setting.protocol === "sio") {
|
||||
this.socket = io(this.setting.serverUrl + "/test");
|
||||
this.socket.on("connect_error", (err) => {
|
||||
this.listener.notifyException(
|
||||
VOICE_CHANGER_CLIENT_EXCEPTION.ERR_SIO_CONNECT_FAILED,
|
||||
`[SIO] rconnection failed ${err}`
|
||||
);
|
||||
});
|
||||
this.socket.on("connect", () => {
|
||||
console.log(`[SIO] connect to ${this.setting.serverUrl}`);
|
||||
console.log(`[SIO] ${this.socket?.id}`);
|
||||
});
|
||||
this.socket.on("close", function (socket) {
|
||||
console.log(`[SIO] close ${socket.id}`);
|
||||
});
|
||||
|
||||
this.socket.on("message", (response: any[]) => {
|
||||
console.log("message:", response);
|
||||
});
|
||||
|
||||
this.socket.on("response", (response: any[]) => {
|
||||
const cur = Date.now();
|
||||
const responseTime = cur - response[0];
|
||||
const result = response[1] as ArrayBuffer;
|
||||
const perf = response[2];
|
||||
|
||||
// Quick hack for server device mode
|
||||
if (response[0] == 0) {
|
||||
this.listener.notifyResponseTime(
|
||||
Math.round(perf[0] * 1000),
|
||||
perf.slice(1, 4)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.byteLength < 128 * 2) {
|
||||
this.listener.notifyException(
|
||||
VOICE_CHANGER_CLIENT_EXCEPTION.ERR_SIO_INVALID_RESPONSE,
|
||||
`[SIO] recevied data is too short ${result.byteLength}`
|
||||
);
|
||||
} else {
|
||||
if (this.outputNode != null) {
|
||||
this.outputNode.postReceivedVoice(response[1]);
|
||||
} else {
|
||||
this.postReceivedVoice(response[1]);
|
||||
}
|
||||
this.listener.notifyResponseTime(responseTime, perf);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
postReceivedVoice = (data: ArrayBuffer) => {
|
||||
// Int16 to Float
|
||||
const i16Data = new Int16Array(data);
|
||||
const f32Data = new Float32Array(i16Data.length);
|
||||
// console.log(`[worklet] f32DataLength${f32Data.length} i16DataLength${i16Data.length}`)
|
||||
i16Data.forEach((x, i) => {
|
||||
const float = x >= 0x8000 ? -(0x10000 - x) / 0x8000 : x / 0x7fff;
|
||||
f32Data[i] = float;
|
||||
});
|
||||
|
||||
// アップサンプリング
|
||||
let upSampledBuffer: Float32Array | null = null;
|
||||
if (this.setting.sendingSampleRate == 48000) {
|
||||
upSampledBuffer = f32Data;
|
||||
} else {
|
||||
upSampledBuffer = new Float32Array(f32Data.length * 2);
|
||||
for (let i = 0; i < f32Data.length; i++) {
|
||||
const currentFrame = f32Data[i];
|
||||
const nextFrame = i + 1 < f32Data.length ? f32Data[i + 1] : f32Data[i];
|
||||
upSampledBuffer[i * 2] = currentFrame;
|
||||
upSampledBuffer[i * 2 + 1] = (currentFrame + nextFrame) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "voice",
|
||||
voice: upSampledBuffer,
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0,
|
||||
};
|
||||
this.port.postMessage(req);
|
||||
|
||||
if (this.isOutputRecording) {
|
||||
this.recordingOutputChunk.push(upSampledBuffer);
|
||||
}
|
||||
};
|
||||
|
||||
private _averageDownsampleBuffer(
|
||||
buffer: Float32Array,
|
||||
originalSampleRate: number,
|
||||
destinationSamplerate: number
|
||||
) {
|
||||
if (originalSampleRate == destinationSamplerate) {
|
||||
return buffer;
|
||||
}
|
||||
if (destinationSamplerate > originalSampleRate) {
|
||||
throw "downsampling rate show be smaller than original sample rate";
|
||||
}
|
||||
const sampleRateRatio = originalSampleRate / destinationSamplerate;
|
||||
const newLength = Math.round(buffer.length / sampleRateRatio);
|
||||
const result = new Float32Array(newLength);
|
||||
let offsetResult = 0;
|
||||
let offsetBuffer = 0;
|
||||
while (offsetResult < result.length) {
|
||||
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
|
||||
// Use average value of skipped samples
|
||||
var accum = 0,
|
||||
count = 0;
|
||||
for (
|
||||
var i = offsetBuffer;
|
||||
i < nextOffsetBuffer && i < buffer.length;
|
||||
i++
|
||||
) {
|
||||
accum += buffer[i];
|
||||
count++;
|
||||
}
|
||||
result[offsetResult] = accum / count;
|
||||
// Or you can simply get rid of the skipped samples:
|
||||
// result[offsetResult] = buffer[nextOffsetBuffer];
|
||||
offsetResult++;
|
||||
offsetBuffer = nextOffsetBuffer;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
handleMessage(event: any) {
|
||||
// console.log(`[Node:handleMessage_] `, event.data.volume);
|
||||
if (event.data.responseType === "start_ok") {
|
||||
if (this.startPromiseResolve) {
|
||||
this.startPromiseResolve();
|
||||
this.startPromiseResolve = null;
|
||||
}
|
||||
} else if (event.data.responseType === "stop_ok") {
|
||||
if (this.stopPromiseResolve) {
|
||||
this.stopPromiseResolve();
|
||||
this.stopPromiseResolve = null;
|
||||
}
|
||||
} else if (event.data.responseType === "volume") {
|
||||
this.listener.notifyVolume(event.data.volume as number);
|
||||
} else if (event.data.responseType === "inputData") {
|
||||
const inputData = event.data.inputData as Float32Array;
|
||||
// console.log("receive input data", inputData);
|
||||
|
||||
// ダウンサンプリング
|
||||
let downsampledBuffer: Float32Array | null = null;
|
||||
if (this.setting.sendingSampleRate == 48000) {
|
||||
downsampledBuffer = inputData;
|
||||
} else if (this.setting.downSamplingMode == DownSamplingMode.decimate) {
|
||||
//////// (Kind 1) 間引き //////////
|
||||
//// 48000Hz で入ってくるので間引いて24000Hzに変換する。
|
||||
downsampledBuffer = new Float32Array(inputData.length / 2);
|
||||
for (let i = 0; i < inputData.length; i++) {
|
||||
if (i % 2 == 0) {
|
||||
downsampledBuffer[i / 2] = inputData[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//////// (Kind 2) 平均 //////////
|
||||
// downsampledBuffer = this._averageDownsampleBuffer(buffer, 48000, 24000)
|
||||
downsampledBuffer = this._averageDownsampleBuffer(
|
||||
inputData,
|
||||
48000,
|
||||
this.setting.sendingSampleRate
|
||||
);
|
||||
}
|
||||
|
||||
// Float to Int16 (internalの場合はfloatのまま行く。)
|
||||
if (this.setting.protocol != "internal") {
|
||||
const arrayBuffer = new ArrayBuffer(downsampledBuffer.length * 2);
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
for (let i = 0; i < downsampledBuffer.length; i++) {
|
||||
let s = Math.max(-1, Math.min(1, downsampledBuffer[i]));
|
||||
s = s < 0 ? s * 0x8000 : s * 0x7fff;
|
||||
dataView.setInt16(i * 2, s, true);
|
||||
}
|
||||
// バッファリング
|
||||
this.requestChunks.push(arrayBuffer);
|
||||
} else {
|
||||
// internal
|
||||
// console.log("downsampledBuffer.buffer", downsampledBuffer.buffer);
|
||||
this.requestChunks.push(downsampledBuffer.buffer);
|
||||
}
|
||||
|
||||
//// リクエストバッファの中身が、リクエスト送信数と違う場合は処理終了。
|
||||
if (this.requestChunks.length < this.setting.inputChunkNum) {
|
||||
return;
|
||||
}
|
||||
|
||||
// リクエスト用の入れ物を作成
|
||||
const windowByteLength = this.requestChunks.reduce((prev, cur) => {
|
||||
return prev + cur.byteLength;
|
||||
}, 0);
|
||||
const newBuffer = new Uint8Array(windowByteLength);
|
||||
|
||||
// リクエストのデータをセット
|
||||
this.requestChunks.reduce((prev, cur) => {
|
||||
newBuffer.set(new Uint8Array(cur), prev);
|
||||
return prev + cur.byteLength;
|
||||
}, 0);
|
||||
|
||||
this.sendBuffer(newBuffer);
|
||||
this.requestChunks = [];
|
||||
|
||||
this.listener.notifySendBufferingTime(Date.now() - this.bufferStart);
|
||||
this.bufferStart = Date.now();
|
||||
} else {
|
||||
console.warn(
|
||||
`[worklet_node][voice-changer-worklet-processor] unknown response ${event.data.responseType}`,
|
||||
event.data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private sendBuffer = async (newBuffer: Uint8Array) => {
|
||||
const timestamp = Date.now();
|
||||
if (this.setting.protocol === "sio") {
|
||||
if (!this.socket) {
|
||||
console.warn(`sio is not initialized`);
|
||||
return;
|
||||
}
|
||||
// console.log("emit!")
|
||||
this.socket.emit("request_message", [timestamp, newBuffer.buffer]);
|
||||
} else if (this.setting.protocol === "rest") {
|
||||
const restClient = new ServerRestClient(this.setting.serverUrl);
|
||||
const res = await restClient.postVoice(timestamp, newBuffer.buffer);
|
||||
if (res.byteLength < 128 * 2) {
|
||||
this.listener.notifyException(
|
||||
VOICE_CHANGER_CLIENT_EXCEPTION.ERR_REST_INVALID_RESPONSE,
|
||||
`[REST] recevied data is too short ${res.byteLength}`
|
||||
);
|
||||
} else {
|
||||
if (this.outputNode != null) {
|
||||
this.outputNode.postReceivedVoice(res);
|
||||
} else {
|
||||
this.postReceivedVoice(res);
|
||||
}
|
||||
this.listener.notifyResponseTime(Date.now() - timestamp);
|
||||
}
|
||||
} else if (this.setting.protocol == "internal") {
|
||||
if (!this.internalCallback) {
|
||||
this.listener.notifyException(
|
||||
VOICE_CHANGER_CLIENT_EXCEPTION.ERR_INTERNAL_AUDIO_PROCESS_CALLBACK_IS_NOT_INITIALIZED,
|
||||
`[AudioWorkletNode] internal audio process callback is not initialized`
|
||||
);
|
||||
return;
|
||||
}
|
||||
// const res = await this.internalCallback.processAudio(newBuffer);
|
||||
// if (res.length < 128 * 2) {
|
||||
// return;
|
||||
// }
|
||||
// if (this.outputNode != null) {
|
||||
// this.outputNode.postReceivedVoice(res.buffer);
|
||||
// } else {
|
||||
// this.postReceivedVoice(res.buffer);
|
||||
// }
|
||||
this.internalCallback.processAudio(newBuffer).then((res) => {
|
||||
if (res.length < 128 * 2) {
|
||||
return;
|
||||
}
|
||||
if (this.outputNode != null) {
|
||||
this.outputNode.postReceivedVoice(res.buffer);
|
||||
} else {
|
||||
this.postReceivedVoice(res.buffer);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw "unknown protocol";
|
||||
}
|
||||
};
|
||||
|
||||
// Worklet操作
|
||||
configure = (setting: WorkletSetting) => {
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "config",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: setting.numTrancateTreshold,
|
||||
volTrancateThreshold: setting.volTrancateThreshold,
|
||||
volTrancateLength: setting.volTrancateLength,
|
||||
};
|
||||
this.port.postMessage(req);
|
||||
};
|
||||
|
||||
start = async () => {
|
||||
const p = new Promise<void>((resolve) => {
|
||||
this.startPromiseResolve = resolve;
|
||||
});
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "start",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0,
|
||||
};
|
||||
this.port.postMessage(req);
|
||||
await p;
|
||||
};
|
||||
stop = async () => {
|
||||
const p = new Promise<void>((resolve) => {
|
||||
this.stopPromiseResolve = resolve;
|
||||
});
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "stop",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0,
|
||||
};
|
||||
this.port.postMessage(req);
|
||||
await p;
|
||||
};
|
||||
trancateBuffer = () => {
|
||||
const req: VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: "trancateBuffer",
|
||||
voice: new Float32Array(1),
|
||||
numTrancateTreshold: 0,
|
||||
volTrancateThreshold: 0,
|
||||
volTrancateLength: 0,
|
||||
};
|
||||
this.port.postMessage(req);
|
||||
};
|
||||
|
||||
startOutputRecording = () => {
|
||||
this.recordingOutputChunk = [];
|
||||
this.isOutputRecording = true;
|
||||
};
|
||||
stopOutputRecording = () => {
|
||||
this.isOutputRecording = false;
|
||||
|
||||
const dataSize = this.recordingOutputChunk.reduce((prev, cur) => {
|
||||
return prev + cur.length;
|
||||
}, 0);
|
||||
const samples = new Float32Array(dataSize);
|
||||
let sampleIndex = 0;
|
||||
for (let i = 0; i < this.recordingOutputChunk.length; i++) {
|
||||
for (let j = 0; j < this.recordingOutputChunk[i].length; j++) {
|
||||
samples[sampleIndex] = this.recordingOutputChunk[i][j];
|
||||
sampleIndex++;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
};
|
||||
}
|
@ -1,20 +1,26 @@
|
||||
|
||||
// (★1) chunk sizeは 128サンプル, 256byte(int16)と定義。
|
||||
// (★2) 256byte(最低バッファサイズ256から間引いた個数x2byte)をchunkとして管理。
|
||||
// 24000sample -> 1sec, 128sample(1chunk) -> 5.333msec
|
||||
// 187.5chunk -> 1sec
|
||||
|
||||
export const VoiceChangerType = {
|
||||
"MMVCv15": "MMVCv15",
|
||||
"MMVCv13": "MMVCv13",
|
||||
MMVCv15: "MMVCv15",
|
||||
MMVCv13: "MMVCv13",
|
||||
"so-vits-svc-40": "so-vits-svc-40",
|
||||
"DDSP-SVC": "DDSP-SVC",
|
||||
"RVC": "RVC",
|
||||
"Diffusion-SVC":"Diffusion-SVC",
|
||||
"Beatrice": "Beatrice"
|
||||
RVC: "RVC",
|
||||
"Diffusion-SVC": "Diffusion-SVC",
|
||||
Beatrice: "Beatrice",
|
||||
LLVC: "LLVC",
|
||||
WebModel: "WebModel",
|
||||
EasyVC: "EasyVC",
|
||||
} as const;
|
||||
export type VoiceChangerType = (typeof VoiceChangerType)[keyof typeof VoiceChangerType];
|
||||
|
||||
} as const
|
||||
export type VoiceChangerType = typeof VoiceChangerType[keyof typeof VoiceChangerType]
|
||||
export const StaticModel = {
|
||||
BeatriceJVS: "Beatrice-JVS",
|
||||
} as const;
|
||||
export type StaticModel = (typeof StaticModel)[keyof typeof StaticModel];
|
||||
|
||||
///////////////////////
|
||||
// サーバセッティング
|
||||
@ -22,342 +28,352 @@ export type VoiceChangerType = typeof VoiceChangerType[keyof typeof VoiceChanger
|
||||
export const InputSampleRate = {
|
||||
"48000": 48000,
|
||||
"44100": 44100,
|
||||
"24000": 24000
|
||||
} as const
|
||||
export type InputSampleRate = typeof InputSampleRate[keyof typeof InputSampleRate]
|
||||
"24000": 24000,
|
||||
} as const;
|
||||
export type InputSampleRate = (typeof InputSampleRate)[keyof typeof InputSampleRate];
|
||||
|
||||
export const ModelSamplingRate = {
|
||||
"48000": 48000,
|
||||
"40000": 40000,
|
||||
"32000": 32000
|
||||
} as const
|
||||
export type ModelSamplingRate = typeof InputSampleRate[keyof typeof InputSampleRate]
|
||||
|
||||
"32000": 32000,
|
||||
} as const;
|
||||
export type ModelSamplingRate = (typeof InputSampleRate)[keyof typeof InputSampleRate];
|
||||
|
||||
export const CrossFadeOverlapSize = {
|
||||
"128": 128,
|
||||
"256": 256,
|
||||
"512": 512,
|
||||
"1024": 1024,
|
||||
"2048": 2048,
|
||||
"4096": 4096,
|
||||
} as const
|
||||
export type CrossFadeOverlapSize = typeof CrossFadeOverlapSize[keyof typeof CrossFadeOverlapSize]
|
||||
} as const;
|
||||
export type CrossFadeOverlapSize = (typeof CrossFadeOverlapSize)[keyof typeof CrossFadeOverlapSize];
|
||||
|
||||
export const F0Detector = {
|
||||
"dio": "dio",
|
||||
"harvest": "harvest",
|
||||
"crepe": "crepe",
|
||||
"crepe_full": "crepe_full",
|
||||
"crepe_tiny": "crepe_tiny",
|
||||
"rmvpe": "rmvpe",
|
||||
"rmvpe_onnx": "rmvpe_onnx",
|
||||
} as const
|
||||
export type F0Detector = typeof F0Detector[keyof typeof F0Detector]
|
||||
dio: "dio",
|
||||
harvest: "harvest",
|
||||
crepe: "crepe",
|
||||
crepe_full: "crepe_full",
|
||||
crepe_tiny: "crepe_tiny",
|
||||
rmvpe: "rmvpe",
|
||||
rmvpe_onnx: "rmvpe_onnx",
|
||||
fcpe: "fcpe",
|
||||
} as const;
|
||||
export type F0Detector = (typeof F0Detector)[keyof typeof F0Detector];
|
||||
|
||||
export const DiffMethod = {
|
||||
"pndm": "pndm",
|
||||
pndm: "pndm",
|
||||
"dpm-solver": "dpm-solver",
|
||||
} as const
|
||||
export type DiffMethod = typeof DiffMethod[keyof typeof DiffMethod]
|
||||
} as const;
|
||||
export type DiffMethod = (typeof DiffMethod)[keyof typeof DiffMethod];
|
||||
|
||||
export const RVCModelType = {
|
||||
"pyTorchRVC": "pyTorchRVC",
|
||||
"pyTorchRVCNono": "pyTorchRVCNono",
|
||||
"pyTorchRVCv2": "pyTorchRVCv2",
|
||||
"pyTorchRVCv2Nono": "pyTorchRVCv2Nono",
|
||||
"pyTorchWebUI": "pyTorchWebUI",
|
||||
"pyTorchWebUINono": "pyTorchWebUINono",
|
||||
"onnxRVC": "onnxRVC",
|
||||
"onnxRVCNono": "onnxRVCNono",
|
||||
} as const
|
||||
export type RVCModelType = typeof RVCModelType[keyof typeof RVCModelType]
|
||||
pyTorchRVC: "pyTorchRVC",
|
||||
pyTorchRVCNono: "pyTorchRVCNono",
|
||||
pyTorchRVCv2: "pyTorchRVCv2",
|
||||
pyTorchRVCv2Nono: "pyTorchRVCv2Nono",
|
||||
pyTorchWebUI: "pyTorchWebUI",
|
||||
pyTorchWebUINono: "pyTorchWebUINono",
|
||||
onnxRVC: "onnxRVC",
|
||||
onnxRVCNono: "onnxRVCNono",
|
||||
} as const;
|
||||
export type RVCModelType = (typeof RVCModelType)[keyof typeof RVCModelType];
|
||||
|
||||
export const ServerSettingKey = {
|
||||
"passThrough":"passThrough",
|
||||
"srcId": "srcId",
|
||||
"dstId": "dstId",
|
||||
"gpu": "gpu",
|
||||
passThrough: "passThrough",
|
||||
srcId: "srcId",
|
||||
dstId: "dstId",
|
||||
gpu: "gpu",
|
||||
|
||||
"crossFadeOffsetRate": "crossFadeOffsetRate",
|
||||
"crossFadeEndRate": "crossFadeEndRate",
|
||||
"crossFadeOverlapSize": "crossFadeOverlapSize",
|
||||
crossFadeOffsetRate: "crossFadeOffsetRate",
|
||||
crossFadeEndRate: "crossFadeEndRate",
|
||||
crossFadeOverlapSize: "crossFadeOverlapSize",
|
||||
|
||||
"framework": "framework",
|
||||
"onnxExecutionProvider": "onnxExecutionProvider",
|
||||
framework: "framework",
|
||||
onnxExecutionProvider: "onnxExecutionProvider",
|
||||
|
||||
"f0Factor": "f0Factor",
|
||||
"f0Detector": "f0Detector",
|
||||
"recordIO": "recordIO",
|
||||
f0Factor: "f0Factor",
|
||||
f0Detector: "f0Detector",
|
||||
recordIO: "recordIO",
|
||||
|
||||
"enableServerAudio": "enableServerAudio",
|
||||
"serverAudioStated": "serverAudioStated",
|
||||
"serverAudioSampleRate": "serverAudioSampleRate",
|
||||
"serverInputAudioSampleRate": "serverInputAudioSampleRate",
|
||||
"serverOutputAudioSampleRate": "serverOutputAudioSampleRate",
|
||||
"serverMonitorAudioSampleRate": "serverMonitorAudioSampleRate",
|
||||
"serverInputAudioBufferSize": "serverInputAudioBufferSize",
|
||||
"serverOutputAudioBufferSize": "serverOutputAudioBufferSize",
|
||||
"serverInputDeviceId": "serverInputDeviceId",
|
||||
"serverOutputDeviceId": "serverOutputDeviceId",
|
||||
"serverMonitorDeviceId": "serverMonitorDeviceId",
|
||||
"serverReadChunkSize": "serverReadChunkSize",
|
||||
"serverInputAudioGain": "serverInputAudioGain",
|
||||
"serverOutputAudioGain": "serverOutputAudioGain",
|
||||
"serverMonitorAudioGain": "serverMonitorAudioGain",
|
||||
enableServerAudio: "enableServerAudio",
|
||||
serverAudioStated: "serverAudioStated",
|
||||
serverAudioSampleRate: "serverAudioSampleRate",
|
||||
serverInputAudioSampleRate: "serverInputAudioSampleRate",
|
||||
serverOutputAudioSampleRate: "serverOutputAudioSampleRate",
|
||||
serverMonitorAudioSampleRate: "serverMonitorAudioSampleRate",
|
||||
serverInputAudioBufferSize: "serverInputAudioBufferSize",
|
||||
serverOutputAudioBufferSize: "serverOutputAudioBufferSize",
|
||||
serverInputDeviceId: "serverInputDeviceId",
|
||||
serverOutputDeviceId: "serverOutputDeviceId",
|
||||
serverMonitorDeviceId: "serverMonitorDeviceId",
|
||||
serverReadChunkSize: "serverReadChunkSize",
|
||||
serverInputAudioGain: "serverInputAudioGain",
|
||||
serverOutputAudioGain: "serverOutputAudioGain",
|
||||
serverMonitorAudioGain: "serverMonitorAudioGain",
|
||||
|
||||
"tran": "tran",
|
||||
"noiseScale": "noiseScale",
|
||||
"predictF0": "predictF0",
|
||||
"silentThreshold": "silentThreshold",
|
||||
"extraConvertSize": "extraConvertSize",
|
||||
"clusterInferRatio": "clusterInferRatio",
|
||||
tran: "tran",
|
||||
noiseScale: "noiseScale",
|
||||
predictF0: "predictF0",
|
||||
silentThreshold: "silentThreshold",
|
||||
extraConvertSize: "extraConvertSize",
|
||||
clusterInferRatio: "clusterInferRatio",
|
||||
|
||||
"indexRatio": "indexRatio",
|
||||
"protect": "protect",
|
||||
"rvcQuality": "rvcQuality",
|
||||
"modelSamplingRate": "modelSamplingRate",
|
||||
"silenceFront": "silenceFront",
|
||||
"modelSlotIndex": "modelSlotIndex",
|
||||
indexRatio: "indexRatio",
|
||||
protect: "protect",
|
||||
rvcQuality: "rvcQuality",
|
||||
modelSamplingRate: "modelSamplingRate",
|
||||
silenceFront: "silenceFront",
|
||||
modelSlotIndex: "modelSlotIndex",
|
||||
|
||||
"useEnhancer": "useEnhancer",
|
||||
"useDiff": "useDiff",
|
||||
useEnhancer: "useEnhancer",
|
||||
useDiff: "useDiff",
|
||||
// "useDiffDpm": "useDiffDpm",
|
||||
"diffMethod": "diffMethod",
|
||||
"useDiffSilence": "useDiffSilence",
|
||||
"diffAcc": "diffAcc",
|
||||
"diffSpkId": "diffSpkId",
|
||||
"kStep": "kStep",
|
||||
"threshold": "threshold",
|
||||
diffMethod: "diffMethod",
|
||||
useDiffSilence: "useDiffSilence",
|
||||
diffAcc: "diffAcc",
|
||||
diffSpkId: "diffSpkId",
|
||||
kStep: "kStep",
|
||||
threshold: "threshold",
|
||||
|
||||
"speedUp": "speedUp",
|
||||
"skipDiffusion": "skipDiffusion",
|
||||
|
||||
"inputSampleRate": "inputSampleRate",
|
||||
"enableDirectML": "enableDirectML",
|
||||
} as const
|
||||
export type ServerSettingKey = typeof ServerSettingKey[keyof typeof ServerSettingKey]
|
||||
speedUp: "speedUp",
|
||||
skipDiffusion: "skipDiffusion",
|
||||
|
||||
inputSampleRate: "inputSampleRate",
|
||||
enableDirectML: "enableDirectML",
|
||||
} as const;
|
||||
export type ServerSettingKey = (typeof ServerSettingKey)[keyof typeof ServerSettingKey];
|
||||
|
||||
export type VoiceChangerServerSetting = {
|
||||
passThrough: boolean
|
||||
srcId: number,
|
||||
dstId: number,
|
||||
gpu: number,
|
||||
passThrough: boolean;
|
||||
srcId: number;
|
||||
dstId: number;
|
||||
gpu: number;
|
||||
|
||||
crossFadeOffsetRate: number,
|
||||
crossFadeEndRate: number,
|
||||
crossFadeOverlapSize: CrossFadeOverlapSize,
|
||||
crossFadeOffsetRate: number;
|
||||
crossFadeEndRate: number;
|
||||
crossFadeOverlapSize: CrossFadeOverlapSize;
|
||||
|
||||
f0Factor: number
|
||||
f0Detector: F0Detector // dio or harvest
|
||||
recordIO: number // 0:off, 1:on
|
||||
f0Factor: number;
|
||||
f0Detector: F0Detector; // dio or harvest
|
||||
recordIO: number; // 0:off, 1:on
|
||||
|
||||
enableServerAudio: number // 0:off, 1:on
|
||||
serverAudioStated: number // 0:off, 1:on
|
||||
serverAudioSampleRate: number
|
||||
serverInputAudioSampleRate: number
|
||||
serverOutputAudioSampleRate: number
|
||||
serverMonitorAudioSampleRate: number
|
||||
serverInputAudioBufferSize: number
|
||||
serverOutputAudioBufferSize: number
|
||||
serverInputDeviceId: number
|
||||
serverOutputDeviceId: number
|
||||
serverMonitorDeviceId: number
|
||||
serverReadChunkSize: number
|
||||
serverInputAudioGain: number
|
||||
serverOutputAudioGain: number
|
||||
serverMonitorAudioGain: number
|
||||
enableServerAudio: number; // 0:off, 1:on
|
||||
serverAudioStated: number; // 0:off, 1:on
|
||||
serverAudioSampleRate: number;
|
||||
serverInputAudioSampleRate: number;
|
||||
serverOutputAudioSampleRate: number;
|
||||
serverMonitorAudioSampleRate: number;
|
||||
serverInputAudioBufferSize: number;
|
||||
serverOutputAudioBufferSize: number;
|
||||
serverInputDeviceId: number;
|
||||
serverOutputDeviceId: number;
|
||||
serverMonitorDeviceId: number;
|
||||
serverReadChunkSize: number;
|
||||
serverInputAudioGain: number;
|
||||
serverOutputAudioGain: number;
|
||||
serverMonitorAudioGain: number;
|
||||
|
||||
tran: number; // so-vits-svc
|
||||
noiseScale: number; // so-vits-svc
|
||||
predictF0: number; // so-vits-svc
|
||||
silentThreshold: number; // so-vits-svc
|
||||
extraConvertSize: number; // so-vits-svc
|
||||
clusterInferRatio: number; // so-vits-svc
|
||||
|
||||
tran: number // so-vits-svc
|
||||
noiseScale: number // so-vits-svc
|
||||
predictF0: number // so-vits-svc
|
||||
silentThreshold: number // so-vits-svc
|
||||
extraConvertSize: number// so-vits-svc
|
||||
clusterInferRatio: number // so-vits-svc
|
||||
indexRatio: number; // RVC
|
||||
protect: number; // RVC
|
||||
rvcQuality: number; // 0:low, 1:high
|
||||
silenceFront: number; // 0:off, 1:on
|
||||
modelSamplingRate: ModelSamplingRate; // 32000,40000,48000
|
||||
modelSlotIndex: number | StaticModel;
|
||||
|
||||
indexRatio: number // RVC
|
||||
protect: number // RVC
|
||||
rvcQuality: number // 0:low, 1:high
|
||||
silenceFront: number // 0:off, 1:on
|
||||
modelSamplingRate: ModelSamplingRate // 32000,40000,48000
|
||||
modelSlotIndex: number,
|
||||
|
||||
useEnhancer: number// DDSP-SVC
|
||||
useDiff: number// DDSP-SVC
|
||||
useEnhancer: number; // DDSP-SVC
|
||||
useDiff: number; // DDSP-SVC
|
||||
// useDiffDpm: number// DDSP-SVC
|
||||
diffMethod: DiffMethod, // DDSP-SVC
|
||||
useDiffSilence: number// DDSP-SVC
|
||||
diffAcc: number// DDSP-SVC
|
||||
diffSpkId: number// DDSP-SVC
|
||||
kStep: number// DDSP-SVC
|
||||
threshold: number// DDSP-SVC
|
||||
diffMethod: DiffMethod; // DDSP-SVC
|
||||
useDiffSilence: number; // DDSP-SVC
|
||||
diffAcc: number; // DDSP-SVC
|
||||
diffSpkId: number; // DDSP-SVC
|
||||
kStep: number; // DDSP-SVC
|
||||
threshold: number; // DDSP-SVC
|
||||
|
||||
speedUp: number // Diffusion-SVC
|
||||
skipDiffusion: number // Diffusion-SVC 0:off, 1:on
|
||||
speedUp: number; // Diffusion-SVC
|
||||
skipDiffusion: number; // Diffusion-SVC 0:off, 1:on
|
||||
|
||||
inputSampleRate: InputSampleRate
|
||||
enableDirectML: number
|
||||
}
|
||||
inputSampleRate: InputSampleRate;
|
||||
enableDirectML: number;
|
||||
};
|
||||
|
||||
type ModelSlot = {
|
||||
slotIndex: number
|
||||
voiceChangerType: VoiceChangerType
|
||||
name: string,
|
||||
description: string,
|
||||
credit: string,
|
||||
termsOfUseUrl: string,
|
||||
iconFile: string
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
slotIndex: number | StaticModel;
|
||||
voiceChangerType: VoiceChangerType;
|
||||
name: string;
|
||||
description: string;
|
||||
credit: string;
|
||||
termsOfUseUrl: string;
|
||||
iconFile: string;
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type RVCModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
indexFile: string,
|
||||
defaultIndexRatio: number,
|
||||
defaultProtect: number,
|
||||
defaultTune: number,
|
||||
modelType: RVCModelType,
|
||||
modelFile: string;
|
||||
indexFile: string;
|
||||
defaultIndexRatio: number;
|
||||
defaultProtect: number;
|
||||
defaultTune: number;
|
||||
modelType: RVCModelType;
|
||||
|
||||
embChannels: number,
|
||||
f0: boolean,
|
||||
samplingRate: number
|
||||
deprecated: boolean
|
||||
}
|
||||
embChannels: number;
|
||||
f0: boolean;
|
||||
samplingRate: number;
|
||||
deprecated: boolean;
|
||||
};
|
||||
|
||||
export type MMVCv13ModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
configFile: string,
|
||||
srcId: number
|
||||
dstId: number
|
||||
modelFile: string;
|
||||
configFile: string;
|
||||
srcId: number;
|
||||
dstId: number;
|
||||
|
||||
samplingRate: number
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
samplingRate: number;
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type MMVCv15ModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
configFile: string,
|
||||
srcId: number
|
||||
dstId: number
|
||||
f0Factor: number
|
||||
samplingRate: number
|
||||
f0: { [key: number]: number }
|
||||
}
|
||||
modelFile: string;
|
||||
configFile: string;
|
||||
srcId: number;
|
||||
dstId: number;
|
||||
f0Factor: number;
|
||||
samplingRate: number;
|
||||
f0: { [key: number]: number };
|
||||
};
|
||||
|
||||
export type SoVitsSvc40ModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
configFile: string,
|
||||
clusterFile: string,
|
||||
dstId: number
|
||||
modelFile: string;
|
||||
configFile: string;
|
||||
clusterFile: string;
|
||||
dstId: number;
|
||||
|
||||
samplingRate: number
|
||||
samplingRate: number;
|
||||
|
||||
defaultTune: number
|
||||
defaultClusterInferRatio: number
|
||||
noiseScale: number
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
defaultTune: number;
|
||||
defaultClusterInferRatio: number;
|
||||
noiseScale: number;
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type DDSPSVCModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
configFile: string,
|
||||
diffModelFile: string
|
||||
diffConfigFile: string
|
||||
dstId: number
|
||||
modelFile: string;
|
||||
configFile: string;
|
||||
diffModelFile: string;
|
||||
diffConfigFile: string;
|
||||
dstId: number;
|
||||
|
||||
samplingRate: number
|
||||
|
||||
defaultTune: number
|
||||
enhancer: boolean
|
||||
diffusion: boolean
|
||||
acc: number
|
||||
kstep: number
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
samplingRate: number;
|
||||
|
||||
defaultTune: number;
|
||||
enhancer: boolean;
|
||||
diffusion: boolean;
|
||||
acc: number;
|
||||
kstep: number;
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type DiffusionSVCModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
dstId: number
|
||||
modelFile: string;
|
||||
dstId: number;
|
||||
|
||||
samplingRate: number
|
||||
|
||||
defaultTune: number
|
||||
defaultKstep : number
|
||||
defaultSpeedup: number
|
||||
kStepMax: number
|
||||
nLayers: number
|
||||
nnLayers: number
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
samplingRate: number;
|
||||
|
||||
defaultTune: number;
|
||||
defaultKstep: number;
|
||||
defaultSpeedup: number;
|
||||
kStepMax: number;
|
||||
nLayers: number;
|
||||
nnLayers: number;
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type BeatriceModelSlot = ModelSlot & {
|
||||
modelFile: string
|
||||
dstId: number
|
||||
modelFile: string;
|
||||
dstId: number;
|
||||
|
||||
speakers: { [key: number]: string }
|
||||
}
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type ModelSlotUnion = RVCModelSlot | MMVCv13ModelSlot | MMVCv15ModelSlot | SoVitsSvc40ModelSlot | DDSPSVCModelSlot | DiffusionSVCModelSlot | BeatriceModelSlot
|
||||
export type LLVCModelSlot = ModelSlot & {
|
||||
modelFile: string;
|
||||
configFile: string;
|
||||
|
||||
speakers: { [key: number]: string };
|
||||
};
|
||||
|
||||
export type WebModelSlot = ModelSlot & {
|
||||
modelFile: string;
|
||||
defaultTune: number;
|
||||
modelType: RVCModelType;
|
||||
f0: boolean;
|
||||
samplingRate: number;
|
||||
};
|
||||
|
||||
export type ModelSlotUnion = RVCModelSlot | MMVCv13ModelSlot | MMVCv15ModelSlot | SoVitsSvc40ModelSlot | DDSPSVCModelSlot | DiffusionSVCModelSlot | BeatriceModelSlot | LLVCModelSlot | WebModelSlot;
|
||||
|
||||
type ServerAudioDevice = {
|
||||
kind: "audioinput" | "audiooutput",
|
||||
index: number,
|
||||
name: string
|
||||
hostAPI: string
|
||||
}
|
||||
kind: "audioinput" | "audiooutput";
|
||||
index: number;
|
||||
name: string;
|
||||
hostAPI: string;
|
||||
};
|
||||
|
||||
export type ServerInfo = VoiceChangerServerSetting & {
|
||||
// コンフィグ対象外 (getInfoで取得のみ可能な情報)
|
||||
status: string
|
||||
modelSlots: ModelSlotUnion[]
|
||||
serverAudioInputDevices: ServerAudioDevice[]
|
||||
serverAudioOutputDevices: ServerAudioDevice[]
|
||||
sampleModels: (RVCSampleModel|DiffusionSVCSampleModel)[]
|
||||
status: string;
|
||||
modelSlots: ModelSlotUnion[];
|
||||
serverAudioInputDevices: ServerAudioDevice[];
|
||||
serverAudioOutputDevices: ServerAudioDevice[];
|
||||
sampleModels: (RVCSampleModel | DiffusionSVCSampleModel)[];
|
||||
gpus: {
|
||||
id: number,
|
||||
name: string,
|
||||
memory: number,
|
||||
}[]
|
||||
maxInputLength: number // MMVCv15
|
||||
id: number;
|
||||
name: string;
|
||||
memory: number;
|
||||
}[];
|
||||
maxInputLength: number; // MMVCv15
|
||||
voiceChangerParams: {
|
||||
model_dir: string
|
||||
}
|
||||
}
|
||||
model_dir: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type SampleModel = {
|
||||
id: string
|
||||
voiceChangerType: VoiceChangerType
|
||||
lang: string
|
||||
tag: string[]
|
||||
name: string
|
||||
modelUrl: string
|
||||
termsOfUseUrl: string
|
||||
icon: string
|
||||
credit: string
|
||||
description: string
|
||||
sampleRate: number
|
||||
modelType: string
|
||||
f0: boolean
|
||||
}
|
||||
|
||||
export type RVCSampleModel =SampleModel & {
|
||||
indexUrl: string
|
||||
featureUrl: string
|
||||
}
|
||||
|
||||
|
||||
export type DiffusionSVCSampleModel =SampleModel & {
|
||||
numOfDiffLayers: number
|
||||
numOfNativeLayers: number
|
||||
maxKStep: number
|
||||
}
|
||||
|
||||
id: string;
|
||||
voiceChangerType: VoiceChangerType;
|
||||
lang: string;
|
||||
tag: string[];
|
||||
name: string;
|
||||
modelUrl: string;
|
||||
termsOfUseUrl: string;
|
||||
icon: string;
|
||||
credit: string;
|
||||
description: string;
|
||||
sampleRate: number;
|
||||
modelType: string;
|
||||
f0: boolean;
|
||||
};
|
||||
|
||||
export type RVCSampleModel = SampleModel & {
|
||||
indexUrl: string;
|
||||
featureUrl: string;
|
||||
};
|
||||
|
||||
export type DiffusionSVCSampleModel = SampleModel & {
|
||||
numOfDiffLayers: number;
|
||||
numOfNativeLayers: number;
|
||||
maxKStep: number;
|
||||
};
|
||||
|
||||
export const DefaultServerSetting: ServerInfo = {
|
||||
// VC Common
|
||||
// VC Common
|
||||
passThrough: false,
|
||||
inputSampleRate: 48000,
|
||||
|
||||
@ -388,7 +404,6 @@ export const DefaultServerSetting: ServerInfo = {
|
||||
dstId: 1,
|
||||
gpu: 0,
|
||||
|
||||
|
||||
f0Factor: 1.0,
|
||||
f0Detector: F0Detector.rmvpe_onnx,
|
||||
|
||||
@ -421,101 +436,101 @@ export const DefaultServerSetting: ServerInfo = {
|
||||
skipDiffusion: 1,
|
||||
|
||||
enableDirectML: 0,
|
||||
//
|
||||
//
|
||||
status: "ok",
|
||||
modelSlots: [],
|
||||
serverAudioInputDevices: [],
|
||||
serverAudioOutputDevices: [],
|
||||
|
||||
maxInputLength: 128 * 2048,
|
||||
maxInputLength: 128 * 2048,
|
||||
voiceChangerParams: {
|
||||
model_dir: ""
|
||||
}
|
||||
}
|
||||
model_dir: "",
|
||||
},
|
||||
};
|
||||
|
||||
///////////////////////
|
||||
// Workletセッティング
|
||||
///////////////////////
|
||||
|
||||
export type WorkletSetting = {
|
||||
numTrancateTreshold: number,
|
||||
volTrancateThreshold: number,
|
||||
volTrancateLength: number
|
||||
}
|
||||
numTrancateTreshold: number;
|
||||
volTrancateThreshold: number;
|
||||
volTrancateLength: number;
|
||||
};
|
||||
///////////////////////
|
||||
// Worklet Nodeセッティング
|
||||
///////////////////////
|
||||
export const Protocol = {
|
||||
"sio": "sio",
|
||||
"rest": "rest",
|
||||
} as const
|
||||
export type Protocol = typeof Protocol[keyof typeof Protocol]
|
||||
sio: "sio",
|
||||
rest: "rest",
|
||||
internal: "internal",
|
||||
} as const;
|
||||
export type Protocol = (typeof Protocol)[keyof typeof Protocol];
|
||||
|
||||
export const SendingSampleRate = {
|
||||
"48000": 48000,
|
||||
"44100": 44100,
|
||||
"24000": 24000
|
||||
} as const
|
||||
export type SendingSampleRate = typeof SendingSampleRate[keyof typeof SendingSampleRate]
|
||||
"24000": 24000,
|
||||
} as const;
|
||||
export type SendingSampleRate = (typeof SendingSampleRate)[keyof typeof SendingSampleRate];
|
||||
|
||||
export const DownSamplingMode = {
|
||||
"decimate": "decimate",
|
||||
"average": "average"
|
||||
} as const
|
||||
export type DownSamplingMode = typeof DownSamplingMode[keyof typeof DownSamplingMode]
|
||||
|
||||
decimate: "decimate",
|
||||
average: "average",
|
||||
} as const;
|
||||
export type DownSamplingMode = (typeof DownSamplingMode)[keyof typeof DownSamplingMode];
|
||||
|
||||
export type WorkletNodeSetting = {
|
||||
serverUrl: string,
|
||||
protocol: Protocol,
|
||||
sendingSampleRate: SendingSampleRate,
|
||||
inputChunkNum: number,
|
||||
downSamplingMode: DownSamplingMode,
|
||||
}
|
||||
|
||||
serverUrl: string;
|
||||
protocol: Protocol;
|
||||
sendingSampleRate: SendingSampleRate;
|
||||
inputChunkNum: number;
|
||||
downSamplingMode: DownSamplingMode;
|
||||
};
|
||||
|
||||
///////////////////////
|
||||
// クライアントセッティング
|
||||
///////////////////////
|
||||
export const SampleRate = {
|
||||
"48000": 48000,
|
||||
} as const
|
||||
export type SampleRate = typeof SampleRate[keyof typeof SampleRate]
|
||||
} as const;
|
||||
export type SampleRate = (typeof SampleRate)[keyof typeof SampleRate];
|
||||
|
||||
export type VoiceChangerClientSetting = {
|
||||
audioInput: string | MediaStream | null,
|
||||
sampleRate: SampleRate, // 48000Hz
|
||||
echoCancel: boolean,
|
||||
noiseSuppression: boolean,
|
||||
noiseSuppression2: boolean
|
||||
audioInput: string | MediaStream | null;
|
||||
sampleRate: SampleRate; // 48000Hz
|
||||
echoCancel: boolean;
|
||||
noiseSuppression: boolean;
|
||||
noiseSuppression2: boolean;
|
||||
|
||||
inputGain: number
|
||||
outputGain: number
|
||||
monitorGain: number
|
||||
inputGain: number;
|
||||
outputGain: number;
|
||||
monitorGain: number;
|
||||
|
||||
passThroughConfirmationSkip: boolean
|
||||
}
|
||||
passThroughConfirmationSkip: boolean;
|
||||
};
|
||||
|
||||
///////////////////////
|
||||
// Client セッティング
|
||||
///////////////////////
|
||||
export type ClientSetting = {
|
||||
workletSetting: WorkletSetting
|
||||
workletNodeSetting: WorkletNodeSetting
|
||||
voiceChangerClientSetting: VoiceChangerClientSetting
|
||||
}
|
||||
workletSetting: WorkletSetting;
|
||||
workletNodeSetting: WorkletNodeSetting;
|
||||
voiceChangerClientSetting: VoiceChangerClientSetting;
|
||||
};
|
||||
export const DefaultClientSettng: ClientSetting = {
|
||||
workletSetting: {
|
||||
// numTrancateTreshold: 512 * 2,
|
||||
numTrancateTreshold: 100,
|
||||
volTrancateThreshold: 0.0005,
|
||||
volTrancateLength: 32
|
||||
volTrancateLength: 32,
|
||||
},
|
||||
workletNodeSetting: {
|
||||
serverUrl: "",
|
||||
protocol: "sio",
|
||||
sendingSampleRate: 48000,
|
||||
inputChunkNum: 48,
|
||||
downSamplingMode: "average"
|
||||
inputChunkNum: 192,
|
||||
downSamplingMode: "average",
|
||||
},
|
||||
voiceChangerClientSetting: {
|
||||
audioInput: null,
|
||||
@ -526,10 +541,9 @@ export const DefaultClientSettng: ClientSetting = {
|
||||
inputGain: 1.0,
|
||||
outputGain: 1.0,
|
||||
monitorGain: 1.0,
|
||||
passThroughConfirmationSkip: false
|
||||
}
|
||||
}
|
||||
|
||||
passThroughConfirmationSkip: false,
|
||||
},
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
// Exceptions
|
||||
@ -538,36 +552,34 @@ export const VOICE_CHANGER_CLIENT_EXCEPTION = {
|
||||
ERR_SIO_CONNECT_FAILED: "ERR_SIO_CONNECT_FAILED",
|
||||
ERR_SIO_INVALID_RESPONSE: "ERR_SIO_INVALID_RESPONSE",
|
||||
ERR_REST_INVALID_RESPONSE: "ERR_REST_INVALID_RESPONSE",
|
||||
ERR_MIC_STREAM_NOT_INITIALIZED: "ERR_MIC_STREAM_NOT_INITIALIZED"
|
||||
|
||||
} as const
|
||||
export type VOICE_CHANGER_CLIENT_EXCEPTION = typeof VOICE_CHANGER_CLIENT_EXCEPTION[keyof typeof VOICE_CHANGER_CLIENT_EXCEPTION]
|
||||
|
||||
ERR_MIC_STREAM_NOT_INITIALIZED: "ERR_MIC_STREAM_NOT_INITIALIZED",
|
||||
ERR_INTERNAL_AUDIO_PROCESS_CALLBACK_IS_NOT_INITIALIZED: "ERR_INTERNAL_AUDIO_PROCESS_CALLBACK_IS_NOT_INITIALIZED",
|
||||
} as const;
|
||||
export type VOICE_CHANGER_CLIENT_EXCEPTION = (typeof VOICE_CHANGER_CLIENT_EXCEPTION)[keyof typeof VOICE_CHANGER_CLIENT_EXCEPTION];
|
||||
|
||||
////////////////////////////////////
|
||||
// indexedDB
|
||||
////////////////////////////////////
|
||||
export const INDEXEDDB_DB_APP_NAME = "INDEXEDDB_KEY_VOICE_CHANGER"
|
||||
export const INDEXEDDB_DB_NAME = "INDEXEDDB_KEY_VOICE_CHANGER_DB"
|
||||
export const INDEXEDDB_KEY_CLIENT = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_CLIENT"
|
||||
export const INDEXEDDB_KEY_SERVER = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_SERVER"
|
||||
export const INDEXEDDB_KEY_MODEL_DATA = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_MODEL_DATA"
|
||||
|
||||
export const INDEXEDDB_DB_APP_NAME = "INDEXEDDB_KEY_VOICE_CHANGER";
|
||||
export const INDEXEDDB_DB_NAME = "INDEXEDDB_KEY_VOICE_CHANGER_DB";
|
||||
export const INDEXEDDB_KEY_CLIENT = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_CLIENT";
|
||||
export const INDEXEDDB_KEY_SERVER = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_SERVER";
|
||||
export const INDEXEDDB_KEY_MODEL_DATA = "INDEXEDDB_KEY_VOICE_CHANGER_LIB_MODEL_DATA";
|
||||
|
||||
// ONNX
|
||||
export type OnnxExporterInfo = {
|
||||
"status": string
|
||||
"path": string
|
||||
"filename": string
|
||||
}
|
||||
status: string;
|
||||
path: string;
|
||||
filename: string;
|
||||
};
|
||||
|
||||
// Merge
|
||||
export type MergeElement = {
|
||||
slotIndex: number
|
||||
strength: number
|
||||
}
|
||||
slotIndex: number;
|
||||
strength: number;
|
||||
};
|
||||
export type MergeModelRequest = {
|
||||
voiceChangerType: VoiceChangerType
|
||||
command: "mix",
|
||||
files: MergeElement[]
|
||||
}
|
||||
voiceChangerType: VoiceChangerType;
|
||||
command: "mix";
|
||||
files: MergeElement[];
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
export class ModelLoadException extends Error {
|
||||
public causeFileType: string = ""
|
||||
public causeFileType: string = "";
|
||||
constructor(causeFileType: string) {
|
||||
super(`Model Load Exception:${causeFileType}`);
|
||||
this.causeFileType = causeFileType;
|
||||
|
@ -1,269 +1,260 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react"
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { useClientSetting } from "./useClientSetting"
|
||||
import { IndexedDBStateAndMethod, useIndexedDB } from "./useIndexedDB"
|
||||
import { ServerSettingState, useServerSetting } from "./useServerSetting"
|
||||
import { useWorkletNodeSetting } from "./useWorkletNodeSetting"
|
||||
import { useWorkletSetting } from "./useWorkletSetting"
|
||||
import { ClientSetting, DefaultClientSettng, VoiceChangerClientSetting, WorkletNodeSetting, WorkletSetting } from "../const"
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
import { useClientSetting } from "./useClientSetting";
|
||||
import { IndexedDBStateAndMethod, useIndexedDB } from "./useIndexedDB";
|
||||
import { ServerSettingState, useServerSetting } from "./useServerSetting";
|
||||
import { useWorkletNodeSetting } from "./useWorkletNodeSetting";
|
||||
import { useWorkletSetting } from "./useWorkletSetting";
|
||||
import { ClientSetting, DefaultClientSettng, VoiceChangerClientSetting, WorkletNodeSetting, WorkletSetting } from "../const";
|
||||
import { InternalCallback } from "../client/VoiceChangerWorkletNode";
|
||||
|
||||
export type UseClientProps = {
|
||||
audioContext: AudioContext | null
|
||||
}
|
||||
audioContext: AudioContext | null;
|
||||
};
|
||||
|
||||
export type ClientState = {
|
||||
initialized: boolean
|
||||
setting: ClientSetting,
|
||||
initialized: boolean;
|
||||
setting: ClientSetting;
|
||||
// 各種設定I/Fへの参照
|
||||
setVoiceChangerClientSetting: (_voiceChangerClientSetting: VoiceChangerClientSetting) => void
|
||||
setVoiceChangerClientSetting: (_voiceChangerClientSetting: VoiceChangerClientSetting) => void;
|
||||
setServerUrl: (url: string) => void;
|
||||
start: () => Promise<void>
|
||||
stop: () => Promise<void>
|
||||
reloadClientSetting: () => Promise<void>
|
||||
start: () => Promise<void>;
|
||||
stop: () => Promise<void>;
|
||||
reloadClientSetting: () => Promise<void>;
|
||||
|
||||
setWorkletNodeSetting: (_workletNodeSetting: WorkletNodeSetting) => void
|
||||
startOutputRecording: () => void
|
||||
stopOutputRecording: () => Promise<Float32Array>
|
||||
trancateBuffer: () => Promise<void>
|
||||
setWorkletNodeSetting: (_workletNodeSetting: WorkletNodeSetting) => void;
|
||||
startOutputRecording: () => void;
|
||||
stopOutputRecording: () => Promise<Float32Array>;
|
||||
trancateBuffer: () => Promise<void>;
|
||||
setInternalAudioProcessCallback: (internalCallback: InternalCallback) => Promise<void>;
|
||||
|
||||
setWorkletSetting: (_workletSetting: WorkletSetting) => void
|
||||
setWorkletSetting: (_workletSetting: WorkletSetting) => void;
|
||||
// workletSetting: WorkletSetting
|
||||
// workletSetting: WorkletSettingState
|
||||
// clientSetting: ClientSettingState
|
||||
// workletNodeSetting: WorkletNodeSettingState
|
||||
serverSetting: ServerSettingState
|
||||
indexedDBState: IndexedDBStateAndMethod
|
||||
serverSetting: ServerSettingState;
|
||||
indexedDBState: IndexedDBStateAndMethod;
|
||||
|
||||
// モニタリングデータ
|
||||
bufferingTime: number;
|
||||
volume: number;
|
||||
performance: PerformanceData
|
||||
updatePerformance: (() => Promise<void>) | null
|
||||
performance: PerformanceData;
|
||||
updatePerformance: (() => Promise<void>) | null;
|
||||
// setClientType: (val: ClientType) => void
|
||||
|
||||
// 情報取得
|
||||
getInfo: () => Promise<void>
|
||||
getInfo: () => Promise<void>;
|
||||
// 設定クリア
|
||||
clearSetting: () => Promise<void>
|
||||
clearSetting: () => Promise<void>;
|
||||
// AudioOutputElement 設定
|
||||
setAudioOutputElementId: (elemId: string) => void
|
||||
setAudioMonitorElementId: (elemId: string) => void
|
||||
setAudioOutputElementId: (elemId: string) => void;
|
||||
setAudioMonitorElementId: (elemId: string) => void;
|
||||
|
||||
ioErrorCount: number
|
||||
resetIoErrorCount: () => void
|
||||
}
|
||||
ioErrorCount: number;
|
||||
resetIoErrorCount: () => void;
|
||||
};
|
||||
|
||||
export type PerformanceData = {
|
||||
responseTime: number
|
||||
preprocessTime: number
|
||||
mainprocessTime: number
|
||||
postprocessTime: number
|
||||
}
|
||||
responseTime: number;
|
||||
preprocessTime: number;
|
||||
mainprocessTime: number;
|
||||
postprocessTime: number;
|
||||
};
|
||||
const InitialPerformanceData: PerformanceData = {
|
||||
responseTime: 0,
|
||||
preprocessTime: 0,
|
||||
mainprocessTime: 0,
|
||||
postprocessTime: 0
|
||||
}
|
||||
postprocessTime: 0,
|
||||
};
|
||||
|
||||
export const useClient = (props: UseClientProps): ClientState => {
|
||||
|
||||
const [initialized, setInitialized] = useState<boolean>(false)
|
||||
const [setting, setSetting] = useState<ClientSetting>(DefaultClientSettng)
|
||||
// (1-1) クライアント
|
||||
const voiceChangerClientRef = useRef<VoiceChangerClient | null>(null)
|
||||
const [voiceChangerClient, setVoiceChangerClient] = useState<VoiceChangerClient | null>(voiceChangerClientRef.current)
|
||||
const [initialized, setInitialized] = useState<boolean>(false);
|
||||
const [setting, setSetting] = useState<ClientSetting>(DefaultClientSettng);
|
||||
// (1-1) クライアント
|
||||
const voiceChangerClientRef = useRef<VoiceChangerClient | null>(null);
|
||||
const [voiceChangerClient, setVoiceChangerClient] = useState<VoiceChangerClient | null>(voiceChangerClientRef.current);
|
||||
//// クライアント初期化待ち用フラグ
|
||||
const initializedResolveRef = useRef<(value: void | PromiseLike<void>) => void>()
|
||||
const initializedResolveRef = useRef<(value: void | PromiseLike<void>) => void>();
|
||||
const initializedPromise = useMemo(() => {
|
||||
return new Promise<void>((resolve) => {
|
||||
initializedResolveRef.current = resolve
|
||||
})
|
||||
}, [])
|
||||
initializedResolveRef.current = resolve;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// (1-2) 各種設定I/F
|
||||
const voiceChangerClientSetting = useClientSetting({ voiceChangerClient, voiceChangerClientSetting: setting.voiceChangerClientSetting })
|
||||
const workletNodeSetting = useWorkletNodeSetting({ voiceChangerClient: voiceChangerClient, workletNodeSetting: setting.workletNodeSetting })
|
||||
useWorkletSetting({ voiceChangerClient, workletSetting: setting.workletSetting })
|
||||
const serverSetting = useServerSetting({ voiceChangerClient })
|
||||
const indexedDBState = useIndexedDB({ clientType: null })
|
||||
|
||||
const voiceChangerClientSetting = useClientSetting({ voiceChangerClient, voiceChangerClientSetting: setting.voiceChangerClientSetting });
|
||||
const workletNodeSetting = useWorkletNodeSetting({ voiceChangerClient: voiceChangerClient, workletNodeSetting: setting.workletNodeSetting });
|
||||
useWorkletSetting({ voiceChangerClient, workletSetting: setting.workletSetting });
|
||||
const serverSetting = useServerSetting({ voiceChangerClient });
|
||||
const indexedDBState = useIndexedDB({ clientType: null });
|
||||
|
||||
// (1-3) モニタリングデータ
|
||||
const [bufferingTime, setBufferingTime] = useState<number>(0)
|
||||
const [performance, setPerformance] = useState<PerformanceData>(InitialPerformanceData)
|
||||
const [volume, setVolume] = useState<number>(0)
|
||||
const [ioErrorCount, setIoErrorCount] = useState<number>(0)
|
||||
const [bufferingTime, setBufferingTime] = useState<number>(0);
|
||||
const [performance, setPerformance] = useState<PerformanceData>(InitialPerformanceData);
|
||||
const [volume, setVolume] = useState<number>(0);
|
||||
const [ioErrorCount, setIoErrorCount] = useState<number>(0);
|
||||
|
||||
//// Server Audio Deviceを使うとき、モニタリングデータはpolling
|
||||
const updatePerformance = useMemo(() => {
|
||||
if (!voiceChangerClientRef.current) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
return async () => {
|
||||
if (voiceChangerClientRef.current) {
|
||||
const performance = await voiceChangerClientRef.current!.getPerformance()
|
||||
const responseTime = performance[0]
|
||||
const preprocessTime = performance[1]
|
||||
const mainprocessTime = performance[2]
|
||||
const postprocessTime = performance[3]
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime })
|
||||
const performance = await voiceChangerClientRef.current!.getPerformance();
|
||||
const responseTime = performance[0];
|
||||
const preprocessTime = performance[1];
|
||||
const mainprocessTime = performance[2];
|
||||
const postprocessTime = performance[3];
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime });
|
||||
} else {
|
||||
const responseTime = 0
|
||||
const preprocessTime = 0
|
||||
const mainprocessTime = 0
|
||||
const postprocessTime = 0
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime })
|
||||
const responseTime = 0;
|
||||
const preprocessTime = 0;
|
||||
const mainprocessTime = 0;
|
||||
const postprocessTime = 0;
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime });
|
||||
}
|
||||
}
|
||||
}, [voiceChangerClientRef.current])
|
||||
|
||||
|
||||
};
|
||||
}, [voiceChangerClientRef.current]);
|
||||
|
||||
// (1-4) エラーステータス
|
||||
const ioErrorCountRef = useRef<number>(0)
|
||||
const ioErrorCountRef = useRef<number>(0);
|
||||
const resetIoErrorCount = () => {
|
||||
ioErrorCountRef.current = 0
|
||||
setIoErrorCount(ioErrorCountRef.current)
|
||||
}
|
||||
ioErrorCountRef.current = 0;
|
||||
setIoErrorCount(ioErrorCountRef.current);
|
||||
};
|
||||
|
||||
// 設定データ管理
|
||||
const { setItem, getItem } = useIndexedDB({ clientType: null })
|
||||
const { setItem, getItem, removeItem } = useIndexedDB({ clientType: null });
|
||||
// 設定データの更新と保存
|
||||
const _setSetting = (_setting: ClientSetting) => {
|
||||
const storeData = { ..._setting }
|
||||
storeData.voiceChangerClientSetting = { ...storeData.voiceChangerClientSetting }
|
||||
const storeData = { ..._setting };
|
||||
storeData.voiceChangerClientSetting = { ...storeData.voiceChangerClientSetting };
|
||||
if (typeof storeData.voiceChangerClientSetting.audioInput != "string") {
|
||||
storeData.voiceChangerClientSetting.audioInput = "none"
|
||||
storeData.voiceChangerClientSetting.audioInput = "none";
|
||||
}
|
||||
setItem("clientSetting", storeData)
|
||||
setItem("clientSetting", storeData);
|
||||
|
||||
setSetting(_setting)
|
||||
}
|
||||
setSetting(_setting);
|
||||
};
|
||||
// 設定データ初期化
|
||||
useEffect(() => {
|
||||
if (!voiceChangerClient) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const loadCache = async () => {
|
||||
const _setting = await getItem("clientSetting") as ClientSetting
|
||||
const _setting = (await getItem("clientSetting")) as ClientSetting;
|
||||
if (_setting) {
|
||||
setSetting(_setting)
|
||||
serverSetting.reloadServerInfo()
|
||||
|
||||
setSetting(_setting);
|
||||
serverSetting.reloadServerInfo();
|
||||
}
|
||||
}
|
||||
loadCache()
|
||||
}, [voiceChangerClient])
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
loadCache();
|
||||
}, [voiceChangerClient]);
|
||||
|
||||
// (2-1) クライアント初期化処理
|
||||
useEffect(() => {
|
||||
const initialized = async () => {
|
||||
if (!props.audioContext) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const voiceChangerClient = new VoiceChangerClient(props.audioContext, true, {
|
||||
notifySendBufferingTime: (val: number) => {
|
||||
setBufferingTime(val)
|
||||
setBufferingTime(val);
|
||||
},
|
||||
notifyResponseTime: (val: number, perf?: number[]) => {
|
||||
const responseTime = val
|
||||
const preprocessTime = perf ? Math.ceil(perf[0] * 1000) : 0
|
||||
const mainprocessTime = perf ? Math.ceil(perf[1] * 1000) : 0
|
||||
const postprocessTime = perf ? Math.ceil(perf[2] * 1000) : 0
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime })
|
||||
const responseTime = val;
|
||||
const preprocessTime = perf ? Math.ceil(perf[0] * 1000) : 0;
|
||||
const mainprocessTime = perf ? Math.ceil(perf[1] * 1000) : 0;
|
||||
const postprocessTime = perf ? Math.ceil(perf[2] * 1000) : 0;
|
||||
setPerformance({ responseTime, preprocessTime, mainprocessTime, postprocessTime });
|
||||
},
|
||||
notifyException: (mes: string) => {
|
||||
if (mes.length > 0) {
|
||||
console.log(`error:${mes}`)
|
||||
ioErrorCountRef.current += 1
|
||||
setIoErrorCount(ioErrorCountRef.current)
|
||||
console.log(`error:${mes}`);
|
||||
ioErrorCountRef.current += 1;
|
||||
setIoErrorCount(ioErrorCountRef.current);
|
||||
}
|
||||
},
|
||||
notifyVolume: (vol: number) => {
|
||||
setVolume(vol)
|
||||
}
|
||||
})
|
||||
setVolume(vol);
|
||||
},
|
||||
});
|
||||
|
||||
await voiceChangerClient.isInitialized()
|
||||
voiceChangerClientRef.current = voiceChangerClient
|
||||
setVoiceChangerClient(voiceChangerClientRef.current)
|
||||
console.log("[useClient] client initialized")
|
||||
await voiceChangerClient.isInitialized();
|
||||
voiceChangerClientRef.current = voiceChangerClient;
|
||||
setVoiceChangerClient(voiceChangerClientRef.current);
|
||||
console.log("[useClient] client initialized");
|
||||
|
||||
// const audio = document.getElementById(props.audioOutputElementId) as HTMLAudioElement
|
||||
// audio.srcObject = voiceChangerClientRef.current.stream
|
||||
// audio.play()
|
||||
initializedResolveRef.current!()
|
||||
setInitialized(true)
|
||||
}
|
||||
initialized()
|
||||
}, [props.audioContext])
|
||||
initializedResolveRef.current!();
|
||||
setInitialized(true);
|
||||
};
|
||||
initialized();
|
||||
}, [props.audioContext]);
|
||||
|
||||
const setAudioOutputElementId = (elemId: string) => {
|
||||
if (!voiceChangerClientRef.current) {
|
||||
console.warn("[voiceChangerClient] is not ready for set audio output.")
|
||||
return
|
||||
console.warn("[voiceChangerClient] is not ready for set audio output.");
|
||||
return;
|
||||
}
|
||||
const audio = document.getElementById(elemId) as HTMLAudioElement
|
||||
const audio = document.getElementById(elemId) as HTMLAudioElement;
|
||||
if (audio.paused) {
|
||||
audio.srcObject = voiceChangerClientRef.current.stream
|
||||
audio.play()
|
||||
audio.srcObject = voiceChangerClientRef.current.stream;
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setAudioMonitorElementId = (elemId: string) => {
|
||||
if (!voiceChangerClientRef.current) {
|
||||
console.warn("[voiceChangerClient] is not ready for set audio output.")
|
||||
return
|
||||
console.warn("[voiceChangerClient] is not ready for set audio output.");
|
||||
return;
|
||||
}
|
||||
const audio = document.getElementById(elemId) as HTMLAudioElement
|
||||
const audio = document.getElementById(elemId) as HTMLAudioElement;
|
||||
if (audio.paused) {
|
||||
audio.srcObject = voiceChangerClientRef.current.monitorStream
|
||||
audio.play()
|
||||
audio.srcObject = voiceChangerClientRef.current.monitorStream;
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// (2-2) 情報リロード
|
||||
const getInfo = useMemo(() => {
|
||||
return async () => {
|
||||
await initializedPromise
|
||||
await voiceChangerClientSetting.reloadClientSetting() // 実質的な処理の意味はない
|
||||
await serverSetting.reloadServerInfo()
|
||||
}
|
||||
}, [voiceChangerClientSetting.reloadClientSetting, serverSetting.reloadServerInfo])
|
||||
|
||||
await initializedPromise;
|
||||
await voiceChangerClientSetting.reloadClientSetting(); // 実質的な処理の意味はない
|
||||
await serverSetting.reloadServerInfo();
|
||||
};
|
||||
}, [voiceChangerClientSetting.reloadClientSetting, serverSetting.reloadServerInfo]);
|
||||
|
||||
const clearSetting = async () => {
|
||||
// TBD
|
||||
}
|
||||
await removeItem("clientSetting");
|
||||
};
|
||||
|
||||
// 設定変更
|
||||
const setVoiceChangerClientSetting = (_voiceChangerClientSetting: VoiceChangerClientSetting) => {
|
||||
setting.voiceChangerClientSetting = _voiceChangerClientSetting
|
||||
console.log("setting.voiceChangerClientSetting", setting.voiceChangerClientSetting)
|
||||
setting.voiceChangerClientSetting = _voiceChangerClientSetting;
|
||||
console.log("setting.voiceChangerClientSetting", setting.voiceChangerClientSetting);
|
||||
// workletSettingIF.setSetting(_workletSetting)
|
||||
_setSetting({ ...setting })
|
||||
}
|
||||
|
||||
_setSetting({ ...setting });
|
||||
};
|
||||
|
||||
const setWorkletNodeSetting = (_workletNodeSetting: WorkletNodeSetting) => {
|
||||
setting.workletNodeSetting = _workletNodeSetting
|
||||
console.log("setting.workletNodeSetting", setting.workletNodeSetting)
|
||||
setting.workletNodeSetting = _workletNodeSetting;
|
||||
console.log("setting.workletNodeSetting", setting.workletNodeSetting);
|
||||
// workletSettingIF.setSetting(_workletSetting)
|
||||
_setSetting({ ...setting })
|
||||
}
|
||||
_setSetting({ ...setting });
|
||||
};
|
||||
|
||||
const setWorkletSetting = (_workletSetting: WorkletSetting) => {
|
||||
setting.workletSetting = _workletSetting
|
||||
console.log("setting.workletSetting", setting.workletSetting)
|
||||
setting.workletSetting = _workletSetting;
|
||||
console.log("setting.workletSetting", setting.workletSetting);
|
||||
// workletSettingIF.setSetting(_workletSetting)
|
||||
_setSetting({ ...setting })
|
||||
}
|
||||
_setSetting({ ...setting });
|
||||
};
|
||||
|
||||
return {
|
||||
initialized,
|
||||
@ -279,6 +270,7 @@ export const useClient = (props: UseClientProps): ClientState => {
|
||||
startOutputRecording: workletNodeSetting.startOutputRecording,
|
||||
stopOutputRecording: workletNodeSetting.stopOutputRecording,
|
||||
trancateBuffer: workletNodeSetting.trancateBuffer,
|
||||
setInternalAudioProcessCallback: workletNodeSetting.setInternalAudioProcessCallback,
|
||||
|
||||
setWorkletSetting,
|
||||
// workletSetting: workletSettingIF.setting,
|
||||
@ -302,6 +294,6 @@ export const useClient = (props: UseClientProps): ClientState => {
|
||||
setAudioMonitorElementId,
|
||||
|
||||
ioErrorCount,
|
||||
resetIoErrorCount
|
||||
}
|
||||
}
|
||||
resetIoErrorCount,
|
||||
};
|
||||
};
|
||||
|
@ -1,49 +1,46 @@
|
||||
import { useState, useMemo, useEffect } from "react"
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
|
||||
import { VoiceChangerClientSetting } from "../const"
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { VoiceChangerClientSetting } from "../const";
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
|
||||
export type UseClientSettingProps = {
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
voiceChangerClientSetting: VoiceChangerClientSetting
|
||||
}
|
||||
voiceChangerClient: VoiceChangerClient | null;
|
||||
voiceChangerClientSetting: VoiceChangerClientSetting;
|
||||
};
|
||||
|
||||
export type ClientSettingState = {
|
||||
|
||||
setServerUrl: (url: string) => void;
|
||||
start: () => Promise<void>
|
||||
stop: () => Promise<void>
|
||||
reloadClientSetting: () => Promise<void>
|
||||
}
|
||||
start: () => Promise<void>;
|
||||
stop: () => Promise<void>;
|
||||
reloadClientSetting: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const useClientSetting = (props: UseClientSettingProps): ClientSettingState => {
|
||||
// 更新比較用
|
||||
const [voiceChangerClientSetting, setVoiceChangerClientSetting] = useState<VoiceChangerClientSetting>(props.voiceChangerClientSetting)
|
||||
const [voiceChangerClientSetting, setVoiceChangerClientSetting] = useState<VoiceChangerClientSetting>(props.voiceChangerClientSetting);
|
||||
|
||||
useEffect(() => {
|
||||
const update = async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
if (!props.voiceChangerClient) return;
|
||||
for (let k in props.voiceChangerClientSetting) {
|
||||
const cur_v = voiceChangerClientSetting[k as keyof VoiceChangerClientSetting]
|
||||
const new_v = props.voiceChangerClientSetting[k as keyof VoiceChangerClientSetting]
|
||||
const cur_v = voiceChangerClientSetting[k as keyof VoiceChangerClientSetting];
|
||||
const new_v = props.voiceChangerClientSetting[k as keyof VoiceChangerClientSetting];
|
||||
if (cur_v != new_v) {
|
||||
setVoiceChangerClientSetting(props.voiceChangerClientSetting)
|
||||
await props.voiceChangerClient.updateClientSetting(props.voiceChangerClientSetting)
|
||||
break
|
||||
setVoiceChangerClientSetting(props.voiceChangerClientSetting);
|
||||
await props.voiceChangerClient.updateClientSetting(props.voiceChangerClientSetting);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
update()
|
||||
}, [props.voiceChangerClient, props.voiceChangerClientSetting])
|
||||
|
||||
};
|
||||
update();
|
||||
}, [props.voiceChangerClient, props.voiceChangerClientSetting]);
|
||||
|
||||
const setServerUrl = useMemo(() => {
|
||||
return (url: string) => {
|
||||
if (!props.voiceChangerClient) return
|
||||
props.voiceChangerClient.setServerUrl(url, true)
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
if (!props.voiceChangerClient) return;
|
||||
props.voiceChangerClient.setServerUrl(url, true);
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
//////////////
|
||||
// 操作
|
||||
@ -51,29 +48,29 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
|
||||
// (1) start
|
||||
const start = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
await props.voiceChangerClient.start()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return;
|
||||
await props.voiceChangerClient.start();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
// (2) stop
|
||||
const stop = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
await props.voiceChangerClient.stop()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return;
|
||||
await props.voiceChangerClient.stop();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
const reloadClientSetting = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
await props.voiceChangerClient.getClientSettings()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return;
|
||||
await props.voiceChangerClient.getClientSettings();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
return {
|
||||
setServerUrl,
|
||||
|
||||
start,
|
||||
stop,
|
||||
reloadClientSetting
|
||||
}
|
||||
}
|
||||
reloadClientSetting,
|
||||
};
|
||||
};
|
||||
|
@ -3,67 +3,65 @@ import { useMemo } from "react";
|
||||
import { INDEXEDDB_DB_APP_NAME, INDEXEDDB_DB_NAME } from "../const";
|
||||
|
||||
export type UseIndexedDBProps = {
|
||||
clientType: null
|
||||
}
|
||||
clientType: null;
|
||||
};
|
||||
export type IndexedDBState = {
|
||||
dummy: string
|
||||
}
|
||||
dummy: string;
|
||||
};
|
||||
export type IndexedDBStateAndMethod = IndexedDBState & {
|
||||
setItem: (key: string, value: unknown) => Promise<void>,
|
||||
getItem: (key: string) => Promise<unknown>
|
||||
removeItem: (key: string) => Promise<void>
|
||||
removeDB: () => Promise<void>
|
||||
}
|
||||
setItem: (key: string, value: unknown) => Promise<void>;
|
||||
getItem: (key: string) => Promise<unknown>;
|
||||
removeItem: (key: string) => Promise<void>;
|
||||
removeDB: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const useIndexedDB = (props: UseIndexedDBProps): IndexedDBStateAndMethod => {
|
||||
const clientType = props.clientType || "default"
|
||||
const clientType = props.clientType || "default";
|
||||
localForage.config({
|
||||
driver: localForage.INDEXEDDB,
|
||||
name: INDEXEDDB_DB_APP_NAME,
|
||||
version: 1.0,
|
||||
storeName: `${INDEXEDDB_DB_NAME}`,
|
||||
description: 'appStorage'
|
||||
|
||||
})
|
||||
description: "appStorage",
|
||||
});
|
||||
|
||||
const setItem = useMemo(() => {
|
||||
return async (key: string, value: unknown) => {
|
||||
const clientKey = `${clientType}_${key}`
|
||||
await localForage.setItem(clientKey, value)
|
||||
}
|
||||
}, [props.clientType])
|
||||
const clientKey = `${clientType}_${key}`;
|
||||
await localForage.setItem(clientKey, value);
|
||||
};
|
||||
}, [props.clientType]);
|
||||
|
||||
const getItem = useMemo(() => {
|
||||
return async (key: string) => {
|
||||
const clientKey = `${clientType}_${key}`
|
||||
return await localForage.getItem(clientKey)
|
||||
}
|
||||
}, [props.clientType])
|
||||
const clientKey = `${clientType}_${key}`;
|
||||
return await localForage.getItem(clientKey);
|
||||
};
|
||||
}, [props.clientType]);
|
||||
|
||||
const removeItem = useMemo(() => {
|
||||
return async (key: string) => {
|
||||
const clientKey = `${clientType}_${key}`
|
||||
console.log("remove key:", clientKey)
|
||||
return await localForage.removeItem(clientKey)
|
||||
}
|
||||
}, [props.clientType])
|
||||
const clientKey = `${clientType}_${key}`;
|
||||
console.log("remove key:", clientKey);
|
||||
return await localForage.removeItem(clientKey);
|
||||
};
|
||||
}, [props.clientType]);
|
||||
|
||||
const removeDB = useMemo(() => {
|
||||
return async () => {
|
||||
const keys = await localForage.keys()
|
||||
const keys = await localForage.keys();
|
||||
for (const key of keys) {
|
||||
console.log("remove key:", key)
|
||||
await localForage.removeItem(key)
|
||||
console.log("remove key:", key);
|
||||
await localForage.removeItem(key);
|
||||
}
|
||||
}
|
||||
}, [props.clientType])
|
||||
|
||||
};
|
||||
}, [props.clientType]);
|
||||
|
||||
return {
|
||||
dummy: "",
|
||||
setItem,
|
||||
getItem,
|
||||
removeItem,
|
||||
removeDB
|
||||
}
|
||||
}
|
||||
removeDB,
|
||||
};
|
||||
};
|
||||
|
@ -1,228 +1,279 @@
|
||||
import { useState, useMemo } from "react"
|
||||
import { VoiceChangerServerSetting, ServerInfo, ServerSettingKey, OnnxExporterInfo, MergeModelRequest, VoiceChangerType, DefaultServerSetting } from "../const"
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { useState, useMemo } from "react";
|
||||
import {
|
||||
VoiceChangerServerSetting,
|
||||
ServerInfo,
|
||||
ServerSettingKey,
|
||||
OnnxExporterInfo,
|
||||
MergeModelRequest,
|
||||
VoiceChangerType,
|
||||
DefaultServerSetting,
|
||||
} from "../const";
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
|
||||
export const ModelAssetName = {
|
||||
iconFile: "iconFile"
|
||||
} as const
|
||||
export type ModelAssetName = typeof ModelAssetName[keyof typeof ModelAssetName]
|
||||
|
||||
iconFile: "iconFile",
|
||||
} as const;
|
||||
export type ModelAssetName =
|
||||
(typeof ModelAssetName)[keyof typeof ModelAssetName];
|
||||
|
||||
export const ModelFileKind = {
|
||||
"mmvcv13Config": "mmvcv13Config",
|
||||
"mmvcv13Model": "mmvcv13Model",
|
||||
"mmvcv15Config": "mmvcv15Config",
|
||||
"mmvcv15Model": "mmvcv15Model",
|
||||
"mmvcv15Correspondence": "mmvcv15Correspondence",
|
||||
mmvcv13Config: "mmvcv13Config",
|
||||
mmvcv13Model: "mmvcv13Model",
|
||||
mmvcv15Config: "mmvcv15Config",
|
||||
mmvcv15Model: "mmvcv15Model",
|
||||
mmvcv15Correspondence: "mmvcv15Correspondence",
|
||||
|
||||
"soVitsSvc40Config": "soVitsSvc40Config",
|
||||
"soVitsSvc40Model": "soVitsSvc40Model",
|
||||
"soVitsSvc40Cluster": "soVitsSvc40Cluster",
|
||||
soVitsSvc40Config: "soVitsSvc40Config",
|
||||
soVitsSvc40Model: "soVitsSvc40Model",
|
||||
soVitsSvc40Cluster: "soVitsSvc40Cluster",
|
||||
|
||||
"rvcModel": "rvcModel",
|
||||
"rvcIndex": "rvcIndex",
|
||||
rvcModel: "rvcModel",
|
||||
rvcIndex: "rvcIndex",
|
||||
|
||||
"ddspSvcModel": "ddspSvcModel",
|
||||
"ddspSvcModelConfig": "ddspSvcModelConfig",
|
||||
"ddspSvcDiffusion": "ddspSvcDiffusion",
|
||||
"ddspSvcDiffusionConfig": "ddspSvcDiffusionConfig",
|
||||
ddspSvcModel: "ddspSvcModel",
|
||||
ddspSvcModelConfig: "ddspSvcModelConfig",
|
||||
ddspSvcDiffusion: "ddspSvcDiffusion",
|
||||
ddspSvcDiffusionConfig: "ddspSvcDiffusionConfig",
|
||||
|
||||
"diffusionSVCModel": "diffusionSVCModel",
|
||||
diffusionSVCModel: "diffusionSVCModel",
|
||||
|
||||
"beatriceModel": "beatriceModel",
|
||||
beatriceModel: "beatriceModel",
|
||||
|
||||
} as const
|
||||
export type ModelFileKind = typeof ModelFileKind[keyof typeof ModelFileKind]
|
||||
llvcModel: "llvcModel",
|
||||
llvcConfig: "llvcConfig",
|
||||
|
||||
easyVCModel: "easyVCModel",
|
||||
} as const;
|
||||
export type ModelFileKind = (typeof ModelFileKind)[keyof typeof ModelFileKind];
|
||||
|
||||
export type ModelFile = {
|
||||
file: File,
|
||||
kind: ModelFileKind
|
||||
dir: string
|
||||
}
|
||||
file: File;
|
||||
kind: ModelFileKind;
|
||||
dir: string;
|
||||
};
|
||||
|
||||
export type ModelUploadSetting = {
|
||||
voiceChangerType: VoiceChangerType,
|
||||
slot: number
|
||||
isSampleMode: boolean
|
||||
sampleId: string | null
|
||||
voiceChangerType: VoiceChangerType;
|
||||
slot: number;
|
||||
isSampleMode: boolean;
|
||||
sampleId: string | null;
|
||||
|
||||
files: ModelFile[]
|
||||
params: any
|
||||
}
|
||||
files: ModelFile[];
|
||||
params: any;
|
||||
};
|
||||
export type ModelFileForServer = Omit<ModelFile, "file"> & {
|
||||
name: string,
|
||||
kind: ModelFileKind
|
||||
}
|
||||
name: string;
|
||||
kind: ModelFileKind;
|
||||
};
|
||||
export type ModelUploadSettingForServer = Omit<ModelUploadSetting, "files"> & {
|
||||
files: ModelFileForServer[]
|
||||
}
|
||||
files: ModelFileForServer[];
|
||||
};
|
||||
|
||||
type AssetUploadSetting = {
|
||||
slot: number
|
||||
name: ModelAssetName
|
||||
file: string
|
||||
}
|
||||
slot: number;
|
||||
name: ModelAssetName;
|
||||
file: string;
|
||||
};
|
||||
|
||||
export type UseServerSettingProps = {
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
}
|
||||
voiceChangerClient: VoiceChangerClient | null;
|
||||
};
|
||||
|
||||
export type ServerSettingState = {
|
||||
serverSetting: ServerInfo
|
||||
updateServerSettings: (setting: ServerInfo) => Promise<void>
|
||||
reloadServerInfo: () => Promise<void>;
|
||||
serverSetting: ServerInfo;
|
||||
updateServerSettings: (setting: ServerInfo) => Promise<void>;
|
||||
reloadServerInfo: () => Promise<void>;
|
||||
|
||||
uploadModel: (setting: ModelUploadSetting) => Promise<void>
|
||||
uploadProgress: number
|
||||
isUploading: boolean
|
||||
uploadModel: (setting: ModelUploadSetting) => Promise<void>;
|
||||
uploadProgress: number;
|
||||
isUploading: boolean;
|
||||
|
||||
getOnnx: () => Promise<OnnxExporterInfo>
|
||||
mergeModel: (request: MergeModelRequest) => Promise<ServerInfo>
|
||||
updateModelDefault: () => Promise<ServerInfo>
|
||||
updateModelInfo: (slot: number, key: string, val: string) => Promise<ServerInfo>
|
||||
uploadAssets: (slot: number, name: ModelAssetName, file: File) => Promise<void>
|
||||
}
|
||||
getOnnx: () => Promise<OnnxExporterInfo>;
|
||||
mergeModel: (request: MergeModelRequest) => Promise<ServerInfo>;
|
||||
updateModelDefault: () => Promise<ServerInfo>;
|
||||
updateModelInfo: (
|
||||
slot: number,
|
||||
key: string,
|
||||
val: string
|
||||
) => Promise<ServerInfo>;
|
||||
uploadAssets: (
|
||||
slot: number,
|
||||
name: ModelAssetName,
|
||||
file: File
|
||||
) => Promise<void>;
|
||||
};
|
||||
|
||||
export const useServerSetting = (props: UseServerSettingProps): ServerSettingState => {
|
||||
const [serverSetting, setServerSetting] = useState<ServerInfo>(DefaultServerSetting)
|
||||
|
||||
//////////////
|
||||
// 設定
|
||||
/////////////
|
||||
const updateServerSettings = useMemo(() => {
|
||||
return async (setting: ServerInfo) => {
|
||||
if (!props.voiceChangerClient) return
|
||||
for (let i = 0; i < Object.values(ServerSettingKey).length; i++) {
|
||||
const k = Object.values(ServerSettingKey)[i] as keyof VoiceChangerServerSetting
|
||||
const cur_v = serverSetting[k]
|
||||
const new_v = setting[k]
|
||||
|
||||
if (cur_v != new_v) {
|
||||
const res = await props.voiceChangerClient.updateServerSettings(k, "" + new_v)
|
||||
setServerSetting(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [props.voiceChangerClient, serverSetting])
|
||||
|
||||
|
||||
|
||||
//////////////
|
||||
// 操作
|
||||
/////////////
|
||||
const [uploadProgress, setUploadProgress] = useState<number>(0)
|
||||
const [isUploading, setIsUploading] = useState<boolean>(false)
|
||||
|
||||
// (e) モデルアップロード
|
||||
const _uploadFile2 = useMemo(() => {
|
||||
return async (file: File, onprogress: (progress: number, end: boolean) => void, dir: string = "") => {
|
||||
if (!props.voiceChangerClient) return
|
||||
const num = await props.voiceChangerClient.uploadFile2(dir, file, onprogress)
|
||||
const res = await props.voiceChangerClient.concatUploadedFile(dir + file.name, num)
|
||||
console.log("uploaded", num, res)
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
// 新しいアップローダ
|
||||
const uploadModel = useMemo(() => {
|
||||
return async (setting: ModelUploadSetting) => {
|
||||
if (!props.voiceChangerClient) {
|
||||
return
|
||||
}
|
||||
|
||||
setUploadProgress(0)
|
||||
setIsUploading(true)
|
||||
|
||||
|
||||
if (setting.isSampleMode == false) {
|
||||
const progRate = 1 / setting.files.length
|
||||
for (let i = 0; i < setting.files.length; i++) {
|
||||
const progOffset = 100 * i * progRate
|
||||
await _uploadFile2(setting.files[i].file, (progress: number, _end: boolean) => {
|
||||
setUploadProgress(progress * progRate + progOffset)
|
||||
}, setting.files[i].dir)
|
||||
}
|
||||
}
|
||||
const params: ModelUploadSettingForServer = {
|
||||
...setting, files: setting.files.map((f) => { return { name: f.file.name, kind: f.kind, dir: f.dir } })
|
||||
}
|
||||
|
||||
const loadPromise = props.voiceChangerClient.loadModel(
|
||||
0,
|
||||
false,
|
||||
JSON.stringify(params),
|
||||
)
|
||||
await loadPromise
|
||||
|
||||
setUploadProgress(0)
|
||||
setIsUploading(false)
|
||||
reloadServerInfo()
|
||||
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
const uploadAssets = useMemo(() => {
|
||||
return async (slot: number, name: ModelAssetName, file: File) => {
|
||||
if (!props.voiceChangerClient) return
|
||||
|
||||
await _uploadFile2(file, (progress: number, _end: boolean) => {
|
||||
console.log(progress, _end)
|
||||
})
|
||||
const assetUploadSetting: AssetUploadSetting = {
|
||||
slot,
|
||||
name,
|
||||
file: file.name
|
||||
}
|
||||
await props.voiceChangerClient.uploadAssets(JSON.stringify(assetUploadSetting))
|
||||
reloadServerInfo()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
|
||||
|
||||
const reloadServerInfo = useMemo(() => {
|
||||
return async () => {
|
||||
|
||||
if (!props.voiceChangerClient) return
|
||||
const res = await props.voiceChangerClient.getServerSettings()
|
||||
setServerSetting(res)
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
|
||||
const getOnnx = async () => {
|
||||
return props.voiceChangerClient!.getOnnx()
|
||||
export const useServerSetting = (
|
||||
props: UseServerSettingProps
|
||||
): ServerSettingState => {
|
||||
const [serverSetting, _setServerSetting] =
|
||||
useState<ServerInfo>(DefaultServerSetting);
|
||||
const setServerSetting = (info: ServerInfo) => {
|
||||
if (!info.modelSlots) {
|
||||
// サーバが情報を空で返したとき。Web版対策
|
||||
return;
|
||||
}
|
||||
_setServerSetting(info);
|
||||
};
|
||||
|
||||
const mergeModel = async (request: MergeModelRequest) => {
|
||||
const serverInfo = await props.voiceChangerClient!.mergeModel(request)
|
||||
setServerSetting(serverInfo)
|
||||
return serverInfo
|
||||
}
|
||||
//////////////
|
||||
// 設定
|
||||
/////////////
|
||||
const updateServerSettings = useMemo(() => {
|
||||
return async (setting: ServerInfo) => {
|
||||
if (!props.voiceChangerClient) return;
|
||||
for (let i = 0; i < Object.values(ServerSettingKey).length; i++) {
|
||||
const k = Object.values(ServerSettingKey)[
|
||||
i
|
||||
] as keyof VoiceChangerServerSetting;
|
||||
const cur_v = serverSetting[k];
|
||||
const new_v = setting[k];
|
||||
|
||||
const updateModelDefault = async () => {
|
||||
const serverInfo = await props.voiceChangerClient!.updateModelDefault()
|
||||
setServerSetting(serverInfo)
|
||||
return serverInfo
|
||||
}
|
||||
const updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
const serverInfo = await props.voiceChangerClient!.updateModelInfo(slot, key, val)
|
||||
setServerSetting(serverInfo)
|
||||
return serverInfo
|
||||
}
|
||||
if (cur_v != new_v) {
|
||||
const res = await props.voiceChangerClient.updateServerSettings(
|
||||
k,
|
||||
"" + new_v
|
||||
);
|
||||
setServerSetting(res);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [props.voiceChangerClient, serverSetting]);
|
||||
|
||||
return {
|
||||
serverSetting,
|
||||
updateServerSettings,
|
||||
reloadServerInfo,
|
||||
//////////////
|
||||
// 操作
|
||||
/////////////
|
||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
||||
const [isUploading, setIsUploading] = useState<boolean>(false);
|
||||
|
||||
uploadModel,
|
||||
uploadProgress,
|
||||
isUploading,
|
||||
getOnnx,
|
||||
mergeModel,
|
||||
updateModelDefault,
|
||||
updateModelInfo,
|
||||
uploadAssets
|
||||
}
|
||||
}
|
||||
// (e) モデルアップロード
|
||||
const _uploadFile2 = useMemo(() => {
|
||||
return async (
|
||||
file: File,
|
||||
onprogress: (progress: number, end: boolean) => void,
|
||||
dir: string = ""
|
||||
) => {
|
||||
if (!props.voiceChangerClient) return;
|
||||
const num = await props.voiceChangerClient.uploadFile2(
|
||||
dir,
|
||||
file,
|
||||
onprogress
|
||||
);
|
||||
const res = await props.voiceChangerClient.concatUploadedFile(
|
||||
dir + file.name,
|
||||
num
|
||||
);
|
||||
console.log("uploaded", num, res);
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
// 新しいアップローダ
|
||||
const uploadModel = useMemo(() => {
|
||||
return async (setting: ModelUploadSetting) => {
|
||||
if (!props.voiceChangerClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadProgress(0);
|
||||
setIsUploading(true);
|
||||
|
||||
if (setting.isSampleMode == false) {
|
||||
const progRate = 1 / setting.files.length;
|
||||
for (let i = 0; i < setting.files.length; i++) {
|
||||
const progOffset = 100 * i * progRate;
|
||||
await _uploadFile2(
|
||||
setting.files[i].file,
|
||||
(progress: number, _end: boolean) => {
|
||||
setUploadProgress(progress * progRate + progOffset);
|
||||
},
|
||||
setting.files[i].dir
|
||||
);
|
||||
}
|
||||
}
|
||||
const params: ModelUploadSettingForServer = {
|
||||
...setting,
|
||||
files: setting.files.map((f) => {
|
||||
return { name: f.file.name, kind: f.kind, dir: f.dir };
|
||||
}),
|
||||
};
|
||||
|
||||
const loadPromise = props.voiceChangerClient.loadModel(
|
||||
0,
|
||||
false,
|
||||
JSON.stringify(params)
|
||||
);
|
||||
await loadPromise;
|
||||
|
||||
setUploadProgress(0);
|
||||
setIsUploading(false);
|
||||
reloadServerInfo();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const uploadAssets = useMemo(() => {
|
||||
return async (slot: number, name: ModelAssetName, file: File) => {
|
||||
if (!props.voiceChangerClient) return;
|
||||
|
||||
await _uploadFile2(file, (progress: number, _end: boolean) => {
|
||||
console.log(progress, _end);
|
||||
});
|
||||
const assetUploadSetting: AssetUploadSetting = {
|
||||
slot,
|
||||
name,
|
||||
file: file.name,
|
||||
};
|
||||
await props.voiceChangerClient.uploadAssets(
|
||||
JSON.stringify(assetUploadSetting)
|
||||
);
|
||||
reloadServerInfo();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const reloadServerInfo = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return;
|
||||
const res = await props.voiceChangerClient.getServerSettings();
|
||||
setServerSetting(res);
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const getOnnx = async () => {
|
||||
return props.voiceChangerClient!.getOnnx();
|
||||
};
|
||||
|
||||
const mergeModel = async (request: MergeModelRequest) => {
|
||||
const serverInfo = await props.voiceChangerClient!.mergeModel(request);
|
||||
setServerSetting(serverInfo);
|
||||
return serverInfo;
|
||||
};
|
||||
|
||||
const updateModelDefault = async () => {
|
||||
const serverInfo = await props.voiceChangerClient!.updateModelDefault();
|
||||
setServerSetting(serverInfo);
|
||||
return serverInfo;
|
||||
};
|
||||
const updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
const serverInfo = await props.voiceChangerClient!.updateModelInfo(
|
||||
slot,
|
||||
key,
|
||||
val
|
||||
);
|
||||
setServerSetting(serverInfo);
|
||||
return serverInfo;
|
||||
};
|
||||
|
||||
return {
|
||||
serverSetting,
|
||||
updateServerSettings,
|
||||
reloadServerInfo,
|
||||
|
||||
uploadModel,
|
||||
uploadProgress,
|
||||
isUploading,
|
||||
getOnnx,
|
||||
mergeModel,
|
||||
updateModelDefault,
|
||||
updateModelInfo,
|
||||
uploadAssets,
|
||||
};
|
||||
};
|
||||
|
@ -1,67 +1,73 @@
|
||||
import { useState, useMemo, useEffect } from "react"
|
||||
|
||||
import { WorkletNodeSetting } from "../const"
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
|
||||
import { WorkletNodeSetting } from "../const";
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
import { InternalCallback } from "../client/VoiceChangerWorkletNode";
|
||||
|
||||
export type UseWorkletNodeSettingProps = {
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
workletNodeSetting: WorkletNodeSetting
|
||||
}
|
||||
voiceChangerClient: VoiceChangerClient | null;
|
||||
workletNodeSetting: WorkletNodeSetting;
|
||||
};
|
||||
|
||||
export type WorkletNodeSettingState = {
|
||||
startOutputRecording: () => void
|
||||
stopOutputRecording: () => Promise<Float32Array>
|
||||
trancateBuffer: () => Promise<void>
|
||||
}
|
||||
startOutputRecording: () => void;
|
||||
stopOutputRecording: () => Promise<Float32Array>;
|
||||
trancateBuffer: () => Promise<void>;
|
||||
setInternalAudioProcessCallback: (internalCallback: InternalCallback) => Promise<void>;
|
||||
};
|
||||
|
||||
export const useWorkletNodeSetting = (props: UseWorkletNodeSettingProps): WorkletNodeSettingState => {
|
||||
// 更新比較用
|
||||
const [workletNodeSetting, _setWorkletNodeSetting] = useState<WorkletNodeSetting>(props.workletNodeSetting)
|
||||
const [workletNodeSetting, _setWorkletNodeSetting] = useState<WorkletNodeSetting>(props.workletNodeSetting);
|
||||
|
||||
//////////////
|
||||
// 設定
|
||||
/////////////
|
||||
useEffect(() => {
|
||||
|
||||
if (!props.voiceChangerClient) return
|
||||
if (!props.voiceChangerClient) return;
|
||||
for (let k in props.workletNodeSetting) {
|
||||
const cur_v = workletNodeSetting[k as keyof WorkletNodeSetting]
|
||||
const new_v = props.workletNodeSetting[k as keyof WorkletNodeSetting]
|
||||
const cur_v = workletNodeSetting[k as keyof WorkletNodeSetting];
|
||||
const new_v = props.workletNodeSetting[k as keyof WorkletNodeSetting];
|
||||
if (cur_v != new_v) {
|
||||
_setWorkletNodeSetting(props.workletNodeSetting)
|
||||
props.voiceChangerClient.updateWorkletNodeSetting(props.workletNodeSetting)
|
||||
break
|
||||
_setWorkletNodeSetting(props.workletNodeSetting);
|
||||
props.voiceChangerClient.updateWorkletNodeSetting(props.workletNodeSetting);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}, [props.voiceChangerClient, props.workletNodeSetting])
|
||||
|
||||
}, [props.voiceChangerClient, props.workletNodeSetting]);
|
||||
|
||||
const startOutputRecording = useMemo(() => {
|
||||
return () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
props.voiceChangerClient.startOutputRecording()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return;
|
||||
props.voiceChangerClient.startOutputRecording();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const stopOutputRecording = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return new Float32Array()
|
||||
return props.voiceChangerClient.stopOutputRecording()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return new Float32Array();
|
||||
return props.voiceChangerClient.stopOutputRecording();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const trancateBuffer = useMemo(() => {
|
||||
return async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
props.voiceChangerClient.trancateBuffer()
|
||||
}
|
||||
}, [props.voiceChangerClient])
|
||||
if (!props.voiceChangerClient) return;
|
||||
props.voiceChangerClient.trancateBuffer();
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
const setInternalAudioProcessCallback = useMemo(() => {
|
||||
return async (internalCallback: InternalCallback) => {
|
||||
if (!props.voiceChangerClient) return;
|
||||
props.voiceChangerClient.setInternalAudioProcessCallback(internalCallback);
|
||||
};
|
||||
}, [props.voiceChangerClient]);
|
||||
|
||||
return {
|
||||
startOutputRecording,
|
||||
stopOutputRecording,
|
||||
trancateBuffer
|
||||
}
|
||||
}
|
||||
trancateBuffer,
|
||||
setInternalAudioProcessCallback,
|
||||
};
|
||||
};
|
||||
|
@ -1,27 +1,24 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { useState, useEffect } from "react";
|
||||
import { WorkletSetting } from "../const";
|
||||
import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
|
||||
export type UseWorkletSettingProps = {
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
workletSetting: WorkletSetting
|
||||
}
|
||||
voiceChangerClient: VoiceChangerClient | null;
|
||||
workletSetting: WorkletSetting;
|
||||
};
|
||||
|
||||
export type WorkletSettingState = {
|
||||
// setting: WorkletSetting;
|
||||
// setSetting: (setting: WorkletSetting) => void;
|
||||
|
||||
}
|
||||
setting: WorkletSetting;
|
||||
_setSetting: (setting: WorkletSetting) => void;
|
||||
};
|
||||
|
||||
export const useWorkletSetting = (props: UseWorkletSettingProps): WorkletSettingState => {
|
||||
const [setting, _setSetting] = useState<WorkletSetting>(props.workletSetting)
|
||||
const [setting, _setSetting] = useState<WorkletSetting>(props.workletSetting);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.voiceChangerClient) return
|
||||
props.voiceChangerClient.configureWorklet(setting)
|
||||
}, [props.voiceChangerClient, props.workletSetting])
|
||||
|
||||
|
||||
if (!props.voiceChangerClient) return;
|
||||
props.voiceChangerClient.configureWorklet(setting);
|
||||
}, [props.voiceChangerClient, props.workletSetting]);
|
||||
|
||||
// // 設定 _setSettingがトリガでuseEffectが呼ばれて、workletに設定が飛ぶ
|
||||
// const setSetting = useMemo(() => {
|
||||
@ -32,7 +29,7 @@ export const useWorkletSetting = (props: UseWorkletSettingProps): WorkletSetting
|
||||
// }, [props.voiceChangerClient])
|
||||
|
||||
return {
|
||||
// setting,
|
||||
// setSetting,
|
||||
}
|
||||
}
|
||||
setting,
|
||||
_setSetting,
|
||||
};
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
export * from "./const"
|
||||
export * from "./exceptions"
|
||||
export * from "./VoiceChangerClient"
|
||||
export * from "./util"
|
||||
export * from "./hooks/useClient"
|
||||
export * from "./hooks/useIndexedDB"
|
||||
export * from "./hooks/useServerSetting"
|
||||
export * from "./const";
|
||||
export * from "./exceptions";
|
||||
export * from "./VoiceChangerClient";
|
||||
export * from "./util";
|
||||
export * from "./hooks/useClient";
|
||||
export * from "./hooks/useIndexedDB";
|
||||
export * from "./hooks/useServerSetting";
|
||||
|
@ -17,32 +17,32 @@ export const fileSelector = async (regex: string) => {
|
||||
const p = new Promise<File>((resolve, reject) => {
|
||||
fileInput.onchange = (e) => {
|
||||
if (e.target instanceof HTMLInputElement == false) {
|
||||
console.log("invalid target!", e.target)
|
||||
reject("invalid target")
|
||||
return null
|
||||
console.log("invalid target!", e.target);
|
||||
reject("invalid target");
|
||||
return null;
|
||||
}
|
||||
const target = e.target as HTMLInputElement
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (!target.files || target.files.length == 0) {
|
||||
reject("no file selected")
|
||||
return null
|
||||
reject("no file selected");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (regex != "" && target.files[0].type.match(regex)) {
|
||||
reject(`not target file type ${target.files[0].type}`);
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
resolve(target.files[0])
|
||||
return null
|
||||
resolve(target.files[0]);
|
||||
return null;
|
||||
};
|
||||
fileInput.click();
|
||||
});
|
||||
return await p
|
||||
}
|
||||
return await p;
|
||||
};
|
||||
|
||||
export const fileSelectorAsDataURL = async (regex: string) => {
|
||||
const f = await fileSelector(regex)
|
||||
const f = await fileSelector(regex);
|
||||
if (!f) {
|
||||
return f
|
||||
return f;
|
||||
}
|
||||
|
||||
const url = await new Promise<string>((resolve) => {
|
||||
@ -52,15 +52,13 @@ export const fileSelectorAsDataURL = async (regex: string) => {
|
||||
resolve(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(f);
|
||||
})
|
||||
return url
|
||||
}
|
||||
|
||||
});
|
||||
return url;
|
||||
};
|
||||
|
||||
export const validateUrl = (url: string) => {
|
||||
|
||||
if (url?.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1)
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
@ -38,4 +38,4 @@ export class BlockingQueue<T> {
|
||||
get length() {
|
||||
return this._promises.length - this._resolvers.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
|
||||
/* ファイル名の大文字小文字を区別 */
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
/* ファイル名の大文字小文字を区別 */
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
/* 型チェック関係のオプション */
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
/* 型チェック関係のオプション */
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
|
||||
/* Module解決方法 */
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
// "isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
/* Module解決方法 */
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
// "isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
// /* 型チェックだけさせたいので出力なし */
|
||||
// "noEmit": true,
|
||||
/* For avoid WebGL2 error */
|
||||
/* https://stackoverflow.com/questions/52846622/error-ts2430-interface-webglrenderingcontext-incorrectly-extends-interface-w */
|
||||
"skipLibCheck": true
|
||||
},
|
||||
/* tscコマンドで読み込むファイルを指定 */
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
// /* 型チェックだけさせたいので出力なし */
|
||||
// "noEmit": true,
|
||||
/* For avoid WebGL2 error */
|
||||
/* https://stackoverflow.com/questions/52846622/error-ts2430-interface-webglrenderingcontext-incorrectly-extends-interface-w */
|
||||
"skipLibCheck": true
|
||||
},
|
||||
/* tscコマンドで読み込むファイルを指定 */
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./worklet/dist",
|
||||
"declaration": true,
|
||||
/* ファイル名の大文字小文字を区別 */
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./worklet/dist",
|
||||
"declaration": true,
|
||||
/* ファイル名の大文字小文字を区別 */
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
/* 型チェック関係のオプション */
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
/* 型チェック関係のオプション */
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
|
||||
/* Module解決方法 */
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
// "isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
/* Module解決方法 */
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
// "isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
// /* 型チェックだけさせたいので出力なし */
|
||||
// "noEmit": true,
|
||||
/* For avoid WebGL2 error */
|
||||
/* https://stackoverflow.com/questions/52846622/error-ts2430-interface-webglrenderingcontext-incorrectly-extends-interface-w */
|
||||
"skipLibCheck": true
|
||||
},
|
||||
/* tscコマンドで読み込むファイルを指定 */
|
||||
"include": ["worklet/src/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
// /* 型チェックだけさせたいので出力なし */
|
||||
// "noEmit": true,
|
||||
/* For avoid WebGL2 error */
|
||||
/* https://stackoverflow.com/questions/52846622/error-ts2430-interface-webglrenderingcontext-incorrectly-extends-interface-w */
|
||||
"skipLibCheck": true
|
||||
},
|
||||
/* tscコマンドで読み込むファイルを指定 */
|
||||
"include": ["worklet/src/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ module.exports = {
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
fallback: {
|
||||
"buffer": require.resolve("buffer/")
|
||||
}
|
||||
buffer: require.resolve("buffer/"),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -32,11 +32,11 @@ module.exports = {
|
||||
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
react: "react",
|
||||
"react-dom": "reactDOM",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js')
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack.common.js");
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
})
|
||||
mode: "development",
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js')
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack.common.js");
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
})
|
||||
mode: "production",
|
||||
});
|
||||
|
@ -27,5 +27,5 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.worklet.common.js')
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack.worklet.common.js");
|
||||
|
||||
const worklet = merge(common, {
|
||||
mode: 'development',
|
||||
})
|
||||
module.exports = [worklet];
|
||||
mode: "development",
|
||||
});
|
||||
module.exports = [worklet];
|
||||
|
@ -1,8 +1,7 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.worklet.common.js')
|
||||
const { merge } = require("webpack-merge");
|
||||
const common = require("./webpack.worklet.common.js");
|
||||
|
||||
const worklet = merge(common, {
|
||||
mode: 'production',
|
||||
})
|
||||
mode: "production",
|
||||
});
|
||||
module.exports = [worklet];
|
||||
|
||||
|
@ -1,128 +1,136 @@
|
||||
export const RequestType = {
|
||||
"voice": "voice",
|
||||
"config": "config",
|
||||
"start": "start",
|
||||
"stop": "stop",
|
||||
"trancateBuffer": "trancateBuffer",
|
||||
} as const
|
||||
export type RequestType = typeof RequestType[keyof typeof RequestType]
|
||||
|
||||
voice: "voice",
|
||||
config: "config",
|
||||
start: "start",
|
||||
stop: "stop",
|
||||
trancateBuffer: "trancateBuffer",
|
||||
} as const;
|
||||
export type RequestType = (typeof RequestType)[keyof typeof RequestType];
|
||||
|
||||
export const ResponseType = {
|
||||
"volume": "volume",
|
||||
"inputData": "inputData",
|
||||
"start_ok": "start_ok",
|
||||
"stop_ok": "stop_ok",
|
||||
} as const
|
||||
export type ResponseType = typeof ResponseType[keyof typeof ResponseType]
|
||||
|
||||
|
||||
volume: "volume",
|
||||
inputData: "inputData",
|
||||
start_ok: "start_ok",
|
||||
stop_ok: "stop_ok",
|
||||
} as const;
|
||||
export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType];
|
||||
|
||||
export type VoiceChangerWorkletProcessorRequest = {
|
||||
requestType: RequestType,
|
||||
voice: Float32Array,
|
||||
numTrancateTreshold: number
|
||||
volTrancateThreshold: number
|
||||
volTrancateLength: number
|
||||
}
|
||||
requestType: RequestType;
|
||||
voice: Float32Array;
|
||||
numTrancateTreshold: number;
|
||||
volTrancateThreshold: number;
|
||||
volTrancateLength: number;
|
||||
};
|
||||
|
||||
export type VoiceChangerWorkletProcessorResponse = {
|
||||
responseType: ResponseType,
|
||||
volume?: number,
|
||||
recordData?: Float32Array[]
|
||||
inputData?: Float32Array
|
||||
}
|
||||
responseType: ResponseType;
|
||||
volume?: number;
|
||||
recordData?: Float32Array[];
|
||||
inputData?: Float32Array;
|
||||
};
|
||||
|
||||
class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
private BLOCK_SIZE = 128
|
||||
private BLOCK_SIZE = 128;
|
||||
private initialized = false;
|
||||
private volume = 0
|
||||
private numTrancateTreshold = 150
|
||||
private volume = 0;
|
||||
// private numTrancateTreshold = 100;
|
||||
// private volTrancateThreshold = 0.0005
|
||||
// private volTrancateLength = 32
|
||||
// private volTrancateCount = 0
|
||||
|
||||
private isRecording = false
|
||||
private isRecording = false;
|
||||
|
||||
playBuffer: Float32Array[] = []
|
||||
playBuffer: Float32Array[] = [];
|
||||
unpushedF32Data: Float32Array = new Float32Array(0);
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
console.log("[AudioWorkletProcessor] created.");
|
||||
this.initialized = true;
|
||||
this.port.onmessage = this.handleMessage.bind(this);
|
||||
}
|
||||
|
||||
calcVol = (data: Float32Array, prevVol: number) => {
|
||||
const sum = data.reduce((prev, cur) => {
|
||||
return prev + cur * cur
|
||||
}, 0)
|
||||
const rms = Math.sqrt(sum / data.length)
|
||||
return Math.max(rms, prevVol * 0.95)
|
||||
}
|
||||
return prev + cur * cur;
|
||||
}, 0);
|
||||
const rms = Math.sqrt(sum / data.length);
|
||||
return Math.max(rms, prevVol * 0.95);
|
||||
};
|
||||
|
||||
trancateBuffer = () => {
|
||||
console.log("[worklet] Buffer truncated")
|
||||
console.log("[worklet] Buffer truncated");
|
||||
while (this.playBuffer.length > 2) {
|
||||
this.playBuffer.shift()
|
||||
this.playBuffer.shift();
|
||||
}
|
||||
}
|
||||
};
|
||||
handleMessage(event: any) {
|
||||
const request = event.data as VoiceChangerWorkletProcessorRequest
|
||||
const request = event.data as VoiceChangerWorkletProcessorRequest;
|
||||
if (request.requestType === "config") {
|
||||
this.numTrancateTreshold = request.numTrancateTreshold
|
||||
// this.numTrancateTreshold = request.numTrancateTreshold;
|
||||
// this.volTrancateLength = request.volTrancateLength
|
||||
// this.volTrancateThreshold = request.volTrancateThreshold
|
||||
console.log("[worklet] worklet configured", request)
|
||||
return
|
||||
console.log("[worklet] worklet configured", request);
|
||||
return;
|
||||
} else if (request.requestType === "start") {
|
||||
if (this.isRecording) {
|
||||
console.warn("[worklet] recoring is already started")
|
||||
return
|
||||
console.warn("[worklet] recoring is already started");
|
||||
return;
|
||||
}
|
||||
this.isRecording = true
|
||||
this.isRecording = true;
|
||||
const startResponse: VoiceChangerWorkletProcessorResponse = {
|
||||
responseType: "start_ok",
|
||||
}
|
||||
};
|
||||
this.port.postMessage(startResponse);
|
||||
return
|
||||
return;
|
||||
} else if (request.requestType === "stop") {
|
||||
if (!this.isRecording) {
|
||||
console.warn("[worklet] recoring is not started")
|
||||
return
|
||||
console.warn("[worklet] recoring is not started");
|
||||
return;
|
||||
}
|
||||
this.isRecording = false
|
||||
this.isRecording = false;
|
||||
const stopResponse: VoiceChangerWorkletProcessorResponse = {
|
||||
responseType: "stop_ok",
|
||||
}
|
||||
};
|
||||
this.port.postMessage(stopResponse);
|
||||
return
|
||||
return;
|
||||
} else if (request.requestType === "trancateBuffer") {
|
||||
this.trancateBuffer()
|
||||
return
|
||||
this.trancateBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.playBuffer.length > this.numTrancateTreshold) {
|
||||
this.trancateBuffer()
|
||||
const f32Data = request.voice;
|
||||
// if (this.playBuffer.length > this.numTrancateTreshold) {
|
||||
// console.log(`[worklet] Truncate ${this.playBuffer.length} > ${this.numTrancateTreshold}`);
|
||||
// this.trancateBuffer();
|
||||
// }
|
||||
if (this.playBuffer.length > (f32Data.length / this.BLOCK_SIZE) * 1.5) {
|
||||
console.log(`[worklet] Truncate ${this.playBuffer.length} > ${f32Data.length / this.BLOCK_SIZE}`);
|
||||
this.trancateBuffer();
|
||||
}
|
||||
|
||||
const f32Data = request.voice
|
||||
const chunkNum = f32Data.length / this.BLOCK_SIZE
|
||||
const concatedF32Data = new Float32Array(this.unpushedF32Data.length + f32Data.length);
|
||||
concatedF32Data.set(this.unpushedF32Data);
|
||||
concatedF32Data.set(f32Data, this.unpushedF32Data.length);
|
||||
|
||||
const chunkNum = Math.floor(concatedF32Data.length / this.BLOCK_SIZE);
|
||||
for (let i = 0; i < chunkNum; i++) {
|
||||
const block = f32Data.slice(i * this.BLOCK_SIZE, (i + 1) * this.BLOCK_SIZE)
|
||||
this.playBuffer.push(block)
|
||||
const block = concatedF32Data.slice(i * this.BLOCK_SIZE, (i + 1) * this.BLOCK_SIZE);
|
||||
this.playBuffer.push(block);
|
||||
}
|
||||
this.unpushedF32Data = concatedF32Data.slice(chunkNum * this.BLOCK_SIZE);
|
||||
}
|
||||
|
||||
|
||||
pushData = (inputData: Float32Array) => {
|
||||
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
||||
responseType: ResponseType.inputData,
|
||||
inputData: inputData
|
||||
}
|
||||
inputData: inputData,
|
||||
};
|
||||
this.port.postMessage(volumeResponse);
|
||||
}
|
||||
};
|
||||
|
||||
process(_inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array>) {
|
||||
if (!this.initialized) {
|
||||
@ -132,15 +140,15 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
|
||||
if (this.isRecording) {
|
||||
if (_inputs.length > 0 && _inputs[0].length > 0) {
|
||||
this.pushData(_inputs[0][0])
|
||||
this.pushData(_inputs[0][0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.playBuffer.length === 0) {
|
||||
// console.log("[worklet] no play buffer")
|
||||
return true
|
||||
// console.log("[worklet] no play buffer");
|
||||
return true;
|
||||
}
|
||||
|
||||
// console.log("[worklet] play buffer");
|
||||
//// 一定期間無音状態が続いている場合はスキップ。
|
||||
// let voice: Float32Array | undefined
|
||||
// while (true) {
|
||||
@ -155,7 +163,6 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
// this.volTrancateCount = 0
|
||||
// }
|
||||
|
||||
|
||||
// // V.1.5.0よりsilent skipで音飛びするようになったので無効化
|
||||
// if (this.volTrancateCount < this.volTrancateLength || this.volTrancateLength < 0) {
|
||||
// break
|
||||
@ -164,22 +171,18 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
// // console.log("silent...skip")
|
||||
// }
|
||||
// }
|
||||
let voice = this.playBuffer.shift()
|
||||
|
||||
|
||||
|
||||
let voice = this.playBuffer.shift();
|
||||
if (voice) {
|
||||
this.volume = this.calcVol(voice, this.volume)
|
||||
this.volume = this.calcVol(voice, this.volume);
|
||||
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
||||
responseType: ResponseType.volume,
|
||||
volume: this.volume
|
||||
}
|
||||
volume: this.volume,
|
||||
};
|
||||
this.port.postMessage(volumeResponse);
|
||||
outputs[0][0].set(voice)
|
||||
outputs[0][0].set(voice);
|
||||
if (outputs[0].length == 2) {
|
||||
outputs[0][1].set(voice)
|
||||
outputs[0][1].set(voice);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
## VC Client for Docker
|
||||
|
||||
[English](./README_en.md)
|
||||
[English](./README_en.md) [Korean](./README_ko.md)
|
||||
|
||||
## ビルド
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
## VC Client for Docker
|
||||
|
||||
[Japanese](./README.md)
|
||||
[Japanese](./README.md)
|
||||
[Korean](./README.md)
|
||||
|
||||
## Build
|
||||
|
||||
|
47
docker_vcclient/README_ko.md
Normal file
47
docker_vcclient/README_ko.md
Normal file
@ -0,0 +1,47 @@
|
||||
## VC Client for Docker
|
||||
|
||||
[Japanese](./README.md) [English](./README_en.md)
|
||||
|
||||
## 빌드
|
||||
|
||||
리포지토리 폴더의 최상위 위치에서
|
||||
|
||||
```
|
||||
npm run build:docker:vcclient
|
||||
```
|
||||
|
||||
## 실행
|
||||
|
||||
리포지토리 폴더의 최상위 위치에서
|
||||
|
||||
```
|
||||
bash start_docker.sh
|
||||
```
|
||||
|
||||
브라우저(Chrome에서만 지원)로 접속하면 화면이 나옵니다.
|
||||
|
||||
## RUN with options
|
||||
|
||||
GPU를 사용하지 않는 경우에는
|
||||
|
||||
```
|
||||
USE_GPU=off bash start_docker.sh
|
||||
```
|
||||
|
||||
포트 번호를 변경하고 싶은 경우에는
|
||||
|
||||
```
|
||||
EX_PORT=<port> bash start_docker.sh
|
||||
```
|
||||
|
||||
로컬 이미지를 사용하고 싶은 경우에는
|
||||
|
||||
```
|
||||
USE_LOCAL=on bash start_docker.sh
|
||||
```
|
||||
|
||||
## Push to Repo (only for devs)
|
||||
|
||||
```
|
||||
npm run push:docker:vcclient
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user