Spaces:
Running on Zero
Running on Zero
feat(voice): switch the avatar voice to 雨晴はう (VOICEVOX style 10)
Browse files- SPEAKER_STYLE_ID 3 -> 10 and CREDIT_STRING "VOICEVOX:雨晴はう" in voice/tts.py; the
model file keeps its historical zundamon.vvm name (upstream 0.vvm carries all four voices)
- ZUNDAMON_TERMS_URL -> AMEHARE_TERMS_URL (https://amehau.com/?page_id=225) in ui/blocks.py,
wired into the terms notice and the About panel; About names Amehare Project
- Tests re-pinned: SPEAKER_STYLE_ID == 10, AMEHARE_TERMS_HOST in test_credits_visible, the
credit assertion and the fixture docstrings
- LICENSES.md credits block and README credits read VOICEVOX:雨晴はう so the ledger, the page
and the deployed assertion move together
- LICENSES.md +1 -1
- README.md +4 -4
- src/japanese_avatar/ui/blocks.py +9 -7
- src/japanese_avatar/voice/tts.py +14 -9
- tests/e2e/test_avatar_loop.py +5 -5
- tests/e2e/test_stage_standalone.py +1 -1
- tests/test_tts_contract.py +1 -1
LICENSES.md
CHANGED
|
@@ -78,7 +78,7 @@ The credit strings the page must render (`tests/e2e/test_avatar_loop.py::test_cr
|
|
| 78 |
checks the footer, the About panel and the server-delivered config for them):
|
| 79 |
|
| 80 |
```credits
|
| 81 |
-
voice: VOICEVOX:
|
| 82 |
avatar: VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. - VRM Public License 1.0
|
| 83 |
dictionary: Dictionary: JMdict (EDRDG, CC BY-SA 4.0)
|
| 84 |
jlpt: JLPT levels: Jonathan Waller's JLPT Resources via stephenmk/yomitan-jlpt-vocab
|
|
|
|
| 78 |
checks the footer, the About panel and the server-delivered config for them):
|
| 79 |
|
| 80 |
```credits
|
| 81 |
+
voice: VOICEVOX:雨晴はう
|
| 82 |
avatar: VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. - VRM Public License 1.0
|
| 83 |
dictionary: Dictionary: JMdict (EDRDG, CC BY-SA 4.0)
|
| 84 |
jlpt: JLPT levels: Jonathan Waller's JLPT Resources via stephenmk/yomitan-jlpt-vocab
|
README.md
CHANGED
|
@@ -67,10 +67,10 @@ uv run pytest tests/e2e/ -q # browser suite (run it whole, neve
|
|
| 67 |
The in-app footer and About panel are the binding credit surfaces; this section mirrors
|
| 68 |
them so the Space's own page carries the same strings.
|
| 69 |
|
| 70 |
-
- **Voice: VOICEVOX:
|
| 71 |
-
[VOICEVOX CORE](https://voicevox.hiroshiba.jp/term/) (software terms) using the
|
| 72 |
-
voice by
|
| 73 |
-
*Synthesised audio is provided under the VOICEVOX and VOICEVOX:
|
| 74 |
using it you agree to comply with them.*
|
| 75 |
- **Avatar: VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. — VRM Public License 1.0**
|
| 76 |
([terms](https://vrm.dev/licenses/1.0/)), from the official VRM specification samples. The
|
|
|
|
| 67 |
The in-app footer and About panel are the binding credit surfaces; this section mirrors
|
| 68 |
them so the Space's own page carries the same strings.
|
| 69 |
|
| 70 |
+
- **Voice: VOICEVOX:雨晴はう** — speech is synthesised with
|
| 71 |
+
[VOICEVOX CORE](https://voicevox.hiroshiba.jp/term/) (software terms) using the 雨晴はう
|
| 72 |
+
voice by Amehare Project ([character terms](https://amehau.com/?page_id=225)).
|
| 73 |
+
*Synthesised audio is provided under the VOICEVOX and VOICEVOX:雨晴はう terms of use; by
|
| 74 |
using it you agree to comply with them.*
|
| 75 |
- **Avatar: VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. — VRM Public License 1.0**
|
| 76 |
([terms](https://vrm.dev/licenses/1.0/)), from the official VRM specification samples. The
|
src/japanese_avatar/ui/blocks.py
CHANGED
|
@@ -76,7 +76,7 @@ SPEED_MAX = 2.0
|
|
| 76 |
SLOWER_SPEED = 0.75
|
| 77 |
|
| 78 |
VOICEVOX_TERMS_URL = "https://voicevox.hiroshiba.jp/term/"
|
| 79 |
-
|
| 80 |
VRM_TERMS_URL = "https://vrm.dev/licenses/1.0/"
|
| 81 |
VRM_CREDIT = "VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. - VRM Public License 1.0"
|
| 82 |
|
|
@@ -417,8 +417,10 @@ ASR_BADGE_HTML = (
|
|
| 417 |
'<div id="asr-tier-text" class="asr-badge">ASR: loads in your browser on the first push</div>'
|
| 418 |
)
|
| 419 |
|
| 420 |
-
# DPLY-04. The exact string VOICEVOX:
|
| 421 |
-
# terms give as their example
|
|
|
|
|
|
|
| 422 |
# (creditNotation: unnecessary) and carried anyway. See docs/VOICEVOX-SETUP.md and docs/ASSETS.md.
|
| 423 |
# Plan 02-11 adds the three Phase 2 credits beside them: the dictionary credit is REQUIRED on
|
| 424 |
# every screen display by the EDRDG licence, the JLPT credit by Waller's CC BY, and the
|
|
@@ -436,19 +438,19 @@ TERMS_NOTICE_HTML = (
|
|
| 436 |
'<div id="terms-notice-text" class="terms-notice">'
|
| 437 |
f"Synthesised audio is provided under the "
|
| 438 |
f'<a href="{VOICEVOX_TERMS_URL}" target="_blank" rel="noopener">VOICEVOX</a> and '
|
| 439 |
-
f'<a href="{
|
| 440 |
"terms of use; by using it you agree to comply with them."
|
| 441 |
"</div>"
|
| 442 |
)
|
| 443 |
|
| 444 |
# The 「アプリの紹介画面」 the character terms ask for: an about screen, findable with a little
|
| 445 |
-
# looking, carrying the full credit set - VOICEVOX:
|
| 446 |
# the runtime libraries. Open on first load so it is on the introduction screen rather than
|
| 447 |
# behind a click.
|
| 448 |
ABOUT_MD = f"""\
|
| 449 |
**Voice** - {CREDIT_STRING}. Speech is synthesised with
|
| 450 |
-
[VOICEVOX CORE]({VOICEVOX_TERMS_URL}) (software terms) using the
|
| 451 |
-
([character terms]({
|
| 452 |
terms; by using it you agree to comply with them.
|
| 453 |
|
| 454 |
**Avatar** - {VRM_CREDIT}. Source: the official VRM specification samples
|
|
|
|
| 76 |
SLOWER_SPEED = 0.75
|
| 77 |
|
| 78 |
VOICEVOX_TERMS_URL = "https://voicevox.hiroshiba.jp/term/"
|
| 79 |
+
AMEHARE_TERMS_URL = "https://amehau.com/?page_id=225"
|
| 80 |
VRM_TERMS_URL = "https://vrm.dev/licenses/1.0/"
|
| 81 |
VRM_CREDIT = "VRM1_Constraint_Twist_Sample (c) 2022 pixiv Inc. - VRM Public License 1.0"
|
| 82 |
|
|
|
|
| 417 |
'<div id="asr-tier-text" class="asr-badge">ASR: loads in your browser on the first push</div>'
|
| 418 |
)
|
| 419 |
|
| 420 |
+
# DPLY-04. The exact string VOICEVOX:雨晴はう, ASCII colon, no spaces - the form the character
|
| 421 |
+
# terms give as their example; the VOICEVOX credit is mandatory under those terms and the
|
| 422 |
+
# character name is optional-but-welcomed, so this one string satisfies both - always visible,
|
| 423 |
+
# with no interaction. The VRM credit is voluntary
|
| 424 |
# (creditNotation: unnecessary) and carried anyway. See docs/VOICEVOX-SETUP.md and docs/ASSETS.md.
|
| 425 |
# Plan 02-11 adds the three Phase 2 credits beside them: the dictionary credit is REQUIRED on
|
| 426 |
# every screen display by the EDRDG licence, the JLPT credit by Waller's CC BY, and the
|
|
|
|
| 438 |
'<div id="terms-notice-text" class="terms-notice">'
|
| 439 |
f"Synthesised audio is provided under the "
|
| 440 |
f'<a href="{VOICEVOX_TERMS_URL}" target="_blank" rel="noopener">VOICEVOX</a> and '
|
| 441 |
+
f'<a href="{AMEHARE_TERMS_URL}" target="_blank" rel="noopener">{CREDIT_STRING}</a> '
|
| 442 |
"terms of use; by using it you agree to comply with them."
|
| 443 |
"</div>"
|
| 444 |
)
|
| 445 |
|
| 446 |
# The 「アプリの紹介画面」 the character terms ask for: an about screen, findable with a little
|
| 447 |
+
# looking, carrying the full credit set - VOICEVOX:雨晴はう again, the VRM, the dictionary and
|
| 448 |
# the runtime libraries. Open on first load so it is on the introduction screen rather than
|
| 449 |
# behind a click.
|
| 450 |
ABOUT_MD = f"""\
|
| 451 |
**Voice** - {CREDIT_STRING}. Speech is synthesised with
|
| 452 |
+
[VOICEVOX CORE]({VOICEVOX_TERMS_URL}) (software terms) using the 雨晴はう voice by Amehare Project
|
| 453 |
+
([character terms]({AMEHARE_TERMS_URL})). The synthesised audio is provided under both sets of
|
| 454 |
terms; by using it you agree to comply with them.
|
| 455 |
|
| 456 |
**Avatar** - {VRM_CREDIT}. Source: the official VRM specification samples
|
src/japanese_avatar/voice/tts.py
CHANGED
|
@@ -38,19 +38,24 @@ from japanese_avatar.voice.models import SynthResult, TurnTimings
|
|
| 38 |
if TYPE_CHECKING: # pragma: no cover - typing only, never imported at runtime
|
| 39 |
from voicevox_core.blocking import Synthesizer
|
| 40 |
|
| 41 |
-
#:
|
| 42 |
-
#: table and confirmed against the metas.json
|
| 43 |
-
#: The same file also carries 四国めたん (2),
|
| 44 |
-
#: credited and used. See docs/VOICEVOX-SETUP.md.
|
| 45 |
-
SPEAKER_STYLE_ID =
|
| 46 |
-
|
| 47 |
-
#: Rendered persistently in the UI.
|
| 48 |
-
#:
|
| 49 |
-
|
|
|
|
|
|
|
| 50 |
|
| 51 |
_SETUP_DOC = "docs/VOICEVOX-SETUP.md"
|
| 52 |
|
| 53 |
_DEFAULT_DICT_DIR = "voicevox/open_jtalk_dic_utf_8-1.11"
|
|
|
|
|
|
|
|
|
|
| 54 |
_DEFAULT_VVM_PATH = "voicevox/model/zundamon.vvm"
|
| 55 |
_DEFAULT_ORT_DIR = "voicevox_runtime"
|
| 56 |
|
|
|
|
| 38 |
if TYPE_CHECKING: # pragma: no cover - typing only, never imported at runtime
|
| 39 |
from voicevox_core.blocking import Synthesizer
|
| 40 |
|
| 41 |
+
#: 雨晴はう, style ノーマル (speaker uuid 3474ee95-c274-47f9-aa1a-8322163d96f1). Resolved from the
|
| 42 |
+
#: voicevox_vvm 0.17.0 release's own character/style table and confirmed against the metas.json
|
| 43 |
+
#: inside voicevox/model/zundamon.vvm. The same file also carries 四国めたん (2), ずんだもん (3) and
|
| 44 |
+
#: 春日部つむぎ (8); only 雨晴はう is credited and used. See docs/VOICEVOX-SETUP.md.
|
| 45 |
+
SPEAKER_STYLE_ID = 10
|
| 46 |
+
|
| 47 |
+
#: Rendered persistently in the UI. The character terms make the VOICEVOX credit mandatory
|
| 48 |
+
#: (「VOICEVOXのクレジット記載は必須となります」) and the character name optional-but-welcomed; this
|
| 49 |
+
#: form - the terms' own example, ASCII colon, no spaces - satisfies both. It is also required on
|
| 50 |
+
#: its own by merely consuming the AudioQuery, so it survives any future engine swap.
|
| 51 |
+
CREDIT_STRING = "VOICEVOX:雨晴はう"
|
| 52 |
|
| 53 |
_SETUP_DOC = "docs/VOICEVOX-SETUP.md"
|
| 54 |
|
| 55 |
_DEFAULT_DICT_DIR = "voicevox/open_jtalk_dic_utf_8-1.11"
|
| 56 |
+
# zundamon.vvm is a historical filename (chosen when ずんだもん was the speaker): the file is
|
| 57 |
+
# upstream voicevox_vvm 0.17.0's 0.vvm and carries all four voices, so the voice switch did not
|
| 58 |
+
# rename it.
|
| 59 |
_DEFAULT_VVM_PATH = "voicevox/model/zundamon.vvm"
|
| 60 |
_DEFAULT_ORT_DIR = "voicevox_runtime"
|
| 61 |
|
tests/e2e/test_avatar_loop.py
CHANGED
|
@@ -523,7 +523,7 @@ def test_ptt_turn(
|
|
| 523 |
):
|
| 524 |
"""VOIC-02. Hold the button, speak Japanese: a transcript appears and the avatar answers.
|
| 525 |
|
| 526 |
-
The microphone is the
|
| 527 |
transcript renders, not that it is correct - the ASR model choice is 01-07's measured
|
| 528 |
decision and real-speech accuracy is a later phase's question.
|
| 529 |
"""
|
|
@@ -886,7 +886,7 @@ def test_asr_wasm_fallback(
|
|
| 886 |
# The exact flow-down sentence rendered next to the Replay control (blocks.py).
|
| 887 |
FLOW_DOWN_SENTENCE = "by using it you agree to comply with them"
|
| 888 |
VOICEVOX_TERMS_HOST = "voicevox.hiroshiba.jp/term"
|
| 889 |
-
|
| 890 |
# Plan 02-11. The links the Phase 2 licences require on the About screen, by the fragment of
|
| 891 |
# the href that identifies each one: the EDRDG licence/project pages (the attribution the
|
| 892 |
# JMdict licence asks a WWW server and an app "About" screen to carry), Waller's site (his
|
|
@@ -965,7 +965,7 @@ def test_credits_visible(page, space_url, warm_space):
|
|
| 965 |
"""
|
| 966 |
credits = licenses_block("credits")
|
| 967 |
voice = credits["voice"]
|
| 968 |
-
assert voice == "VOICEVOX:
|
| 969 |
assert set(credits) >= {"voice", "avatar", "dictionary", "jlpt", "translation"}, (
|
| 970 |
f"LICENSES.md ```credits block lost a key: {sorted(credits)}"
|
| 971 |
)
|
|
@@ -984,7 +984,7 @@ def test_credits_visible(page, space_url, warm_space):
|
|
| 984 |
assert voice in notice_text
|
| 985 |
notice_links = notice.locator("a").evaluate_all("els => els.map((e) => e.href)")
|
| 986 |
assert any(VOICEVOX_TERMS_HOST in h for h in notice_links), notice_links
|
| 987 |
-
assert any(
|
| 988 |
|
| 989 |
replay = page.locator("#replay-button")
|
| 990 |
assert replay.is_visible()
|
|
@@ -1005,7 +1005,7 @@ def test_credits_visible(page, space_url, warm_space):
|
|
| 1005 |
about_text = about.inner_text()
|
| 1006 |
about_links = about.locator("a").evaluate_all("els => els.map((e) => e.href)")
|
| 1007 |
assert any(VOICEVOX_TERMS_HOST in h for h in about_links), about_links
|
| 1008 |
-
assert any(
|
| 1009 |
|
| 1010 |
# The attribution links the Phase 2 licences require, each named by LICENSES.md.
|
| 1011 |
missing_links = [
|
|
|
|
| 523 |
):
|
| 524 |
"""VOIC-02. Hold the button, speak Japanese: a transcript appears and the avatar answers.
|
| 525 |
|
| 526 |
+
The microphone is the 雨晴はう speech fixture. Phase 1's bar is that a non-empty
|
| 527 |
transcript renders, not that it is correct - the ASR model choice is 01-07's measured
|
| 528 |
decision and real-speech accuracy is a later phase's question.
|
| 529 |
"""
|
|
|
|
| 886 |
# The exact flow-down sentence rendered next to the Replay control (blocks.py).
|
| 887 |
FLOW_DOWN_SENTENCE = "by using it you agree to comply with them"
|
| 888 |
VOICEVOX_TERMS_HOST = "voicevox.hiroshiba.jp/term"
|
| 889 |
+
AMEHARE_TERMS_HOST = "amehau.com/?page_id=225"
|
| 890 |
# Plan 02-11. The links the Phase 2 licences require on the About screen, by the fragment of
|
| 891 |
# the href that identifies each one: the EDRDG licence/project pages (the attribution the
|
| 892 |
# JMdict licence asks a WWW server and an app "About" screen to carry), Waller's site (his
|
|
|
|
| 965 |
"""
|
| 966 |
credits = licenses_block("credits")
|
| 967 |
voice = credits["voice"]
|
| 968 |
+
assert voice == "VOICEVOX:雨晴はう", f"LICENSES.md declares the voice credit as {voice!r}"
|
| 969 |
assert set(credits) >= {"voice", "avatar", "dictionary", "jlpt", "translation"}, (
|
| 970 |
f"LICENSES.md ```credits block lost a key: {sorted(credits)}"
|
| 971 |
)
|
|
|
|
| 984 |
assert voice in notice_text
|
| 985 |
notice_links = notice.locator("a").evaluate_all("els => els.map((e) => e.href)")
|
| 986 |
assert any(VOICEVOX_TERMS_HOST in h for h in notice_links), notice_links
|
| 987 |
+
assert any(AMEHARE_TERMS_HOST in h for h in notice_links), notice_links
|
| 988 |
|
| 989 |
replay = page.locator("#replay-button")
|
| 990 |
assert replay.is_visible()
|
|
|
|
| 1005 |
about_text = about.inner_text()
|
| 1006 |
about_links = about.locator("a").evaluate_all("els => els.map((e) => e.href)")
|
| 1007 |
assert any(VOICEVOX_TERMS_HOST in h for h in about_links), about_links
|
| 1008 |
+
assert any(AMEHARE_TERMS_HOST in h for h in about_links), about_links
|
| 1009 |
|
| 1010 |
# The attribution links the Phase 2 licences require, each named by LICENSES.md.
|
| 1011 |
missing_links = [
|
tests/e2e/test_stage_standalone.py
CHANGED
|
@@ -179,7 +179,7 @@ def test_arms_rest_at_sides(page, static_server):
|
|
| 179 |
|
| 180 |
|
| 181 |
def test_demo_timeline_drives_visemes(page, static_server):
|
| 182 |
-
"""A generated timeline against the real
|
| 183 |
|
| 184 |
Since plan 01-06 the demo timeline is produced by ``visemes.build_timeline`` from a real
|
| 185 |
``AudioQuery`` and the audio is the matching synthesis, so this is no longer a plausible
|
|
|
|
| 179 |
|
| 180 |
|
| 181 |
def test_demo_timeline_drives_visemes(page, static_server):
|
| 182 |
+
"""A generated timeline against the real 雨晴はう WAV, clocked off the AudioContext.
|
| 183 |
|
| 184 |
Since plan 01-06 the demo timeline is produced by ``visemes.build_timeline`` from a real
|
| 185 |
``AudioQuery`` and the audio is the matching synthesis, so this is no longer a plausible
|
tests/test_tts_contract.py
CHANGED
|
@@ -105,7 +105,7 @@ def test_speed_scale_lengthens_audio(synth):
|
|
| 105 |
def test_output_sampling_rate_is_24000(short_result):
|
| 106 |
"""93.75 fps in plan 01-06 is exactly 24000 / 256; the rate is load-bearing, not incidental."""
|
| 107 |
assert short_result.audio_query["outputSamplingRate"] == 24000
|
| 108 |
-
assert SPEAKER_STYLE_ID ==
|
| 109 |
|
| 110 |
|
| 111 |
def test_fixtures_match_current_engine(synth_meta, synth):
|
|
|
|
| 105 |
def test_output_sampling_rate_is_24000(short_result):
|
| 106 |
"""93.75 fps in plan 01-06 is exactly 24000 / 256; the rate is load-bearing, not incidental."""
|
| 107 |
assert short_result.audio_query["outputSamplingRate"] == 24000
|
| 108 |
+
assert SPEAKER_STYLE_ID == 10
|
| 109 |
|
| 110 |
|
| 111 |
def test_fixtures_match_current_engine(synth_meta, synth):
|