Spaces:
Running on Zero
test(01-04): capture ground-truth synthesis fixtures for AVTR-02
Browse files- make_synth_fixtures.py generates three cases and refuses to write a 'long'
fixture without 20+ moras, a devoiced vowel and a pause mora
- Durations and frame counts come from the WAV header, never from summing the query
- Establish the correct frame-quantisation formula empirically: VOICEVOX quantises
each phoneme at speed 1.0 and then divides the frame count by speedScale, rather
than dividing the length first. Verified 24/24 across 6 speeds and 4 sentences;
the length-first form is right only 8/24 and is off by up to 5 frames
- The generator asserts that formula reproduces each fixture's true frame count,
so the fixtures cannot drift away from the engine unnoticed
- Add a regression guard that fires if a voicevox_core bump changes timings
- Memoise synthesis in the test module: it was doing six identical syntheses and
had pushed the quick loop to ~20s against a 15s budget; now ~11s
- Write fixture JSON with explicit LF so regeneration is byte-identical across OSes
- docs/VOICEVOX-SETUP.md +65 -6
- tests/fixtures/audio_query_long.json +355 -0
- tests/fixtures/audio_query_short.json +60 -0
- tests/fixtures/audio_query_slow.json +355 -0
- tests/fixtures/make_synth_fixtures.py +218 -0
- tests/fixtures/speech_ja.wav +3 -0
- tests/fixtures/speech_ja_long.wav +3 -0
- tests/fixtures/speech_ja_slow.wav +3 -0
- tests/fixtures/synth_meta.json +121 -0
- tests/test_tts_contract.py +38 -7
|
@@ -74,13 +74,72 @@ institutions are genuinely both involved and RESEARCH conflated them.
|
|
| 74 |
Everything else RESEARCH inferred was correct, including the whole `Onnxruntime` β `OpenJtalk` β
|
| 75 |
`Synthesizer` β `VoiceModelFile` construction order and the exact Linux wheel filename.
|
| 76 |
|
| 77 |
-
|
|
|
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
---
|
| 86 |
|
|
|
|
| 74 |
Everything else RESEARCH inferred was correct, including the whole `Onnxruntime` β `OpenJtalk` β
|
| 75 |
`Synthesizer` β `VoiceModelFile` construction order and the exact Linux wheel filename.
|
| 76 |
|
| 77 |
+
**6. `speedScale` is applied to the frame count, not to the phoneme length.** This is the most
|
| 78 |
+
consequential correction in this document and it has its own section below.
|
| 79 |
|
| 80 |
+
---
|
| 81 |
+
|
| 82 |
+
## Frame quantisation β read this before writing `visemes.py`
|
| 83 |
+
|
| 84 |
+
**Plan 01-06 depends on this section.** 01-RESEARCH.md's `build_timeline` gets the order wrong,
|
| 85 |
+
and the error is invisible at normal speed, which is exactly what makes it dangerous.
|
| 86 |
+
|
| 87 |
+
RESEARCH's pipeline divides each phoneme length by `speedScale` and *then* quantises:
|
| 88 |
+
|
| 89 |
+
```python
|
| 90 |
+
frames = round(length / speed_scale * 93.75) # WRONG
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
VOICEVOX CORE 0.17.0 actually quantises **first**, at speed 1.0, and then divides the resulting
|
| 94 |
+
**frame count** and rounds again:
|
| 95 |
+
|
| 96 |
+
```python
|
| 97 |
+
frames = round(round(length * 93.75) / speed_scale) # CORRECT
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
At `speedScale == 1.0` the two are identical, so RESEARCH's version looks verified. They diverge
|
| 101 |
+
everywhere else.
|
| 102 |
+
|
| 103 |
+
Measured: 4 sentences Γ 6 speed values (1.0, 0.9, 0.75, 0.5, 1.25, 1.5), predicted total frames
|
| 104 |
+
compared against the true frame count of the synthesised WAV (`getnframes() // 256`):
|
| 105 |
+
|
| 106 |
+
| Formula | Correct |
|
| 107 |
+
|---|---|
|
| 108 |
+
| `round(length / speed * 93.75)` (01-RESEARCH.md) | **8 / 24** |
|
| 109 |
+
| `round(round(length * 93.75) / speed)` | **24 / 24** |
|
| 110 |
+
|
| 111 |
+
Worst observed error from the wrong formula: **5 frames β 53 ms** on the long sentence at
|
| 112 |
+
`speedScale = 1.25`. That is well past the Β±1 frame (10.667 ms) tolerance `test_no_drift_long_utterance`
|
| 113 |
+
is specified with, and it is exactly the symptom `01-RESEARCH.md` Pitfall 5 describes β "normal
|
| 114 |
+
speed syncs, slow speed drifts" β reached from the other direction.
|
| 115 |
+
|
| 116 |
+
Everything else about the pipeline is confirmed and should be implemented as RESEARCH describes:
|
| 117 |
+
|
| 118 |
+
- Flatten each accent phrase's `moras`, then its `pause_mora` if present β pause **after**.
|
| 119 |
+
- Wrap the whole sequence in `prePhonemeLength` / `postPhonemeLength` silence moras, and scale
|
| 120 |
+
those by `speedScale` like any other phoneme. They are not exempt.
|
| 121 |
+
- Emit the consonant (mouth closed) before the vowel within a mora.
|
| 122 |
+
- Accumulate the quantised frame counts, never the raw floats. Per-phoneme rounding error is up to
|
| 123 |
+
Β±0.5 frame β Β±5.3 ms, and the long fixture has 61 phonemes.
|
| 124 |
+
- Use banker's rounding. Python's built-in `round()` already is; JavaScript's `Math.round()` is
|
| 125 |
+
round-half-up and would disagree, which is an independent reason the timeline is built in Python
|
| 126 |
+
and the browser only plays it.
|
| 127 |
+
- Steps involving `pauseLength` / `pauseLengthScale` are no-ops β those fields do not exist here.
|
| 128 |
+
|
| 129 |
+
The committed fixtures are the proof: `tests/fixtures/make_synth_fixtures.py` asserts the correct
|
| 130 |
+
formula reproduces each fixture's true frame count before it will write anything, so the fixture
|
| 131 |
+
set cannot silently drift away from the engine.
|
| 132 |
+
|
| 133 |
+
| Fixture | speedScale | Frames | Duration (s) |
|
| 134 |
+
|---|---|---|---|
|
| 135 |
+
| `short` | 1.0 | 99 | 1.056 |
|
| 136 |
+
| `long` | 1.0 | 516 | 5.504 |
|
| 137 |
+
| `slow` | 0.75 | 692 | 7.381333333333333 |
|
| 138 |
+
|
| 139 |
+
Note that `slow / long = 1.341085`, **not** exactly `1/0.75 = 1.333333`. Re-quantisation after
|
| 140 |
+
scaling means the realised ratio lands near the requested one, not on it. A test that asserts
|
| 141 |
+
"exactly 1/0.75Γ longer" against *durations* will fail; assert the timeline matches the WAV
|
| 142 |
+
instead, which is the property that actually matters for lip-sync.
|
| 143 |
|
| 144 |
---
|
| 145 |
|
|
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"accent_phrases": [
|
| 3 |
+
{
|
| 4 |
+
"accent": 1,
|
| 5 |
+
"is_interrogative": false,
|
| 6 |
+
"moras": [
|
| 7 |
+
{
|
| 8 |
+
"consonant": "ky",
|
| 9 |
+
"consonant_length": 0.1269165426492691,
|
| 10 |
+
"pitch": 5.891244888305664,
|
| 11 |
+
"text": "γγ§",
|
| 12 |
+
"vowel": "o",
|
| 13 |
+
"vowel_length": 0.10178578644990921
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"consonant": null,
|
| 17 |
+
"consonant_length": null,
|
| 18 |
+
"pitch": 5.944635391235352,
|
| 19 |
+
"text": "γͺ",
|
| 20 |
+
"vowel": "o",
|
| 21 |
+
"vowel_length": 0.08867011219263077
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"consonant": "w",
|
| 25 |
+
"consonant_length": 0.06420903652906418,
|
| 26 |
+
"pitch": 5.655149459838867,
|
| 27 |
+
"text": "γ―",
|
| 28 |
+
"vowel": "a",
|
| 29 |
+
"vowel_length": 0.13292233645915985
|
| 30 |
+
}
|
| 31 |
+
],
|
| 32 |
+
"pause_mora": null
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"accent": 1,
|
| 36 |
+
"is_interrogative": false,
|
| 37 |
+
"moras": [
|
| 38 |
+
{
|
| 39 |
+
"consonant": null,
|
| 40 |
+
"consonant_length": null,
|
| 41 |
+
"pitch": 5.756601810455322,
|
| 42 |
+
"text": "γ€",
|
| 43 |
+
"vowel": "i",
|
| 44 |
+
"vowel_length": 0.09258951991796494
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"consonant": null,
|
| 48 |
+
"consonant_length": null,
|
| 49 |
+
"pitch": 6.103951930999756,
|
| 50 |
+
"text": "γ€",
|
| 51 |
+
"vowel": "i",
|
| 52 |
+
"vowel_length": 0.0974084809422493
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
"pause_mora": null
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"accent": 1,
|
| 59 |
+
"is_interrogative": false,
|
| 60 |
+
"moras": [
|
| 61 |
+
{
|
| 62 |
+
"consonant": "t",
|
| 63 |
+
"consonant_length": 0.0657225176692009,
|
| 64 |
+
"pitch": 6.139893531799316,
|
| 65 |
+
"text": "γ",
|
| 66 |
+
"vowel": "e",
|
| 67 |
+
"vowel_length": 0.1257137954235077
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"consonant": null,
|
| 71 |
+
"consonant_length": null,
|
| 72 |
+
"pitch": 6.0533294677734375,
|
| 73 |
+
"text": "γ³",
|
| 74 |
+
"vowel": "N",
|
| 75 |
+
"vowel_length": 0.07083306461572647
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"consonant": "k",
|
| 79 |
+
"consonant_length": 0.05886319279670715,
|
| 80 |
+
"pitch": 5.938107967376709,
|
| 81 |
+
"text": "γ",
|
| 82 |
+
"vowel": "i",
|
| 83 |
+
"vowel_length": 0.059421248733997345
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"consonant": "d",
|
| 87 |
+
"consonant_length": 0.05466592311859131,
|
| 88 |
+
"pitch": 5.8197479248046875,
|
| 89 |
+
"text": "γ",
|
| 90 |
+
"vowel": "e",
|
| 91 |
+
"vowel_length": 0.09957551956176758
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"consonant": "s",
|
| 95 |
+
"consonant_length": 0.03665236383676529,
|
| 96 |
+
"pitch": 0.0,
|
| 97 |
+
"text": "γΉ",
|
| 98 |
+
"vowel": "U",
|
| 99 |
+
"vowel_length": 0.06477490812540054
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"consonant": "k",
|
| 103 |
+
"consonant_length": 0.07497167587280273,
|
| 104 |
+
"pitch": 5.6952314376831055,
|
| 105 |
+
"text": "γ«",
|
| 106 |
+
"vowel": "a",
|
| 107 |
+
"vowel_length": 0.09379018098115921
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"consonant": "r",
|
| 111 |
+
"consonant_length": 0.03401697427034378,
|
| 112 |
+
"pitch": 5.5977702140808105,
|
| 113 |
+
"text": "γ©",
|
| 114 |
+
"vowel": "a",
|
| 115 |
+
"vowel_length": 0.14935533702373505
|
| 116 |
+
}
|
| 117 |
+
],
|
| 118 |
+
"pause_mora": {
|
| 119 |
+
"consonant": null,
|
| 120 |
+
"consonant_length": null,
|
| 121 |
+
"pitch": 0.0,
|
| 122 |
+
"text": "γ",
|
| 123 |
+
"vowel": "pau",
|
| 124 |
+
"vowel_length": 0.2590501606464386
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"accent": 5,
|
| 129 |
+
"is_interrogative": false,
|
| 130 |
+
"moras": [
|
| 131 |
+
{
|
| 132 |
+
"consonant": "k",
|
| 133 |
+
"consonant_length": 0.08955883234739304,
|
| 134 |
+
"pitch": 5.693568229675293,
|
| 135 |
+
"text": "γ³",
|
| 136 |
+
"vowel": "o",
|
| 137 |
+
"vowel_length": 0.09677395969629288
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"consonant": null,
|
| 141 |
+
"consonant_length": null,
|
| 142 |
+
"pitch": 5.938162803649902,
|
| 143 |
+
"text": "γͺ",
|
| 144 |
+
"vowel": "o",
|
| 145 |
+
"vowel_length": 0.12056692689657211
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"consonant": null,
|
| 149 |
+
"consonant_length": null,
|
| 150 |
+
"pitch": 6.016718864440918,
|
| 151 |
+
"text": "γ¨",
|
| 152 |
+
"vowel": "e",
|
| 153 |
+
"vowel_length": 0.147250235080719
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"consonant": null,
|
| 157 |
+
"consonant_length": null,
|
| 158 |
+
"pitch": 6.010623931884766,
|
| 159 |
+
"text": "γ³",
|
| 160 |
+
"vowel": "N",
|
| 161 |
+
"vowel_length": 0.08805551379919052
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"consonant": null,
|
| 165 |
+
"consonant_length": null,
|
| 166 |
+
"pitch": 6.007989883422852,
|
| 167 |
+
"text": "γͺ",
|
| 168 |
+
"vowel": "o",
|
| 169 |
+
"vowel_length": 0.10105019062757492
|
| 170 |
+
}
|
| 171 |
+
],
|
| 172 |
+
"pause_mora": null
|
| 173 |
+
},
|
| 174 |
+
{
|
| 175 |
+
"accent": 3,
|
| 176 |
+
"is_interrogative": false,
|
| 177 |
+
"moras": [
|
| 178 |
+
{
|
| 179 |
+
"consonant": "s",
|
| 180 |
+
"consonant_length": 0.0925176665186882,
|
| 181 |
+
"pitch": 6.003979206085205,
|
| 182 |
+
"text": "γ΅",
|
| 183 |
+
"vowel": "a",
|
| 184 |
+
"vowel_length": 0.1288621425628662
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"consonant": null,
|
| 188 |
+
"consonant_length": null,
|
| 189 |
+
"pitch": 6.026742935180664,
|
| 190 |
+
"text": "γ³",
|
| 191 |
+
"vowel": "N",
|
| 192 |
+
"vowel_length": 0.0830387994647026
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"consonant": "p",
|
| 196 |
+
"consonant_length": 0.054095521569252014,
|
| 197 |
+
"pitch": 6.099804878234863,
|
| 198 |
+
"text": "γ",
|
| 199 |
+
"vowel": "o",
|
| 200 |
+
"vowel_length": 0.1004747673869133
|
| 201 |
+
}
|
| 202 |
+
],
|
| 203 |
+
"pause_mora": null
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"accent": 4,
|
| 207 |
+
"is_interrogative": false,
|
| 208 |
+
"moras": [
|
| 209 |
+
{
|
| 210 |
+
"consonant": "sh",
|
| 211 |
+
"consonant_length": 0.021739566698670387,
|
| 212 |
+
"pitch": 0.0,
|
| 213 |
+
"text": "γ·",
|
| 214 |
+
"vowel": "I",
|
| 215 |
+
"vowel_length": 0.04718482494354248
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"consonant": "t",
|
| 219 |
+
"consonant_length": 0.05912453681230545,
|
| 220 |
+
"pitch": 6.083391189575195,
|
| 221 |
+
"text": "γ",
|
| 222 |
+
"vowel": "e",
|
| 223 |
+
"vowel_length": 0.08641223609447479
|
| 224 |
+
},
|
| 225 |
+
{
|
| 226 |
+
"consonant": "k",
|
| 227 |
+
"consonant_length": 0.05805716663599014,
|
| 228 |
+
"pitch": 6.061047554016113,
|
| 229 |
+
"text": "γ«",
|
| 230 |
+
"vowel": "a",
|
| 231 |
+
"vowel_length": 0.0868045762181282
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"consonant": "r",
|
| 235 |
+
"consonant_length": 0.03226054459810257,
|
| 236 |
+
"pitch": 5.876008987426758,
|
| 237 |
+
"text": "γ©",
|
| 238 |
+
"vowel": "a",
|
| 239 |
+
"vowel_length": 0.14383982121944427
|
| 240 |
+
}
|
| 241 |
+
],
|
| 242 |
+
"pause_mora": {
|
| 243 |
+
"consonant": null,
|
| 244 |
+
"consonant_length": null,
|
| 245 |
+
"pitch": 0.0,
|
| 246 |
+
"text": "γ",
|
| 247 |
+
"vowel": "pau",
|
| 248 |
+
"vowel_length": 0.2888171374797821
|
| 249 |
+
}
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"accent": 5,
|
| 253 |
+
"is_interrogative": false,
|
| 254 |
+
"moras": [
|
| 255 |
+
{
|
| 256 |
+
"consonant": "k",
|
| 257 |
+
"consonant_length": 0.0854499563574791,
|
| 258 |
+
"pitch": 5.536592483520508,
|
| 259 |
+
"text": "γ«",
|
| 260 |
+
"vowel": "a",
|
| 261 |
+
"vowel_length": 0.12839564681053162
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"consonant": null,
|
| 265 |
+
"consonant_length": null,
|
| 266 |
+
"pitch": 5.821318626403809,
|
| 267 |
+
"text": "γ€",
|
| 268 |
+
"vowel": "i",
|
| 269 |
+
"vowel_length": 0.07545183598995209
|
| 270 |
+
},
|
| 271 |
+
{
|
| 272 |
+
"consonant": "m",
|
| 273 |
+
"consonant_length": 0.05912942439317703,
|
| 274 |
+
"pitch": 6.023046493530273,
|
| 275 |
+
"text": "γ’",
|
| 276 |
+
"vowel": "o",
|
| 277 |
+
"vowel_length": 0.0851563960313797
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
"consonant": "n",
|
| 281 |
+
"consonant_length": 0.05413999408483505,
|
| 282 |
+
"pitch": 6.078396797180176,
|
| 283 |
+
"text": "γ",
|
| 284 |
+
"vowel": "o",
|
| 285 |
+
"vowel_length": 0.08871601521968842
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"consonant": "n",
|
| 289 |
+
"consonant_length": 0.07090981304645538,
|
| 290 |
+
"pitch": 6.0716552734375,
|
| 291 |
+
"text": "γ",
|
| 292 |
+
"vowel": "i",
|
| 293 |
+
"vowel_length": 0.0862032100558281
|
| 294 |
+
}
|
| 295 |
+
],
|
| 296 |
+
"pause_mora": null
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"accent": 3,
|
| 300 |
+
"is_interrogative": false,
|
| 301 |
+
"moras": [
|
| 302 |
+
{
|
| 303 |
+
"consonant": null,
|
| 304 |
+
"consonant_length": null,
|
| 305 |
+
"pitch": 6.005620956420898,
|
| 306 |
+
"text": "γ€",
|
| 307 |
+
"vowel": "i",
|
| 308 |
+
"vowel_length": 0.09876994043588638
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
"consonant": "k",
|
| 312 |
+
"consonant_length": 0.07177173346281052,
|
| 313 |
+
"pitch": 6.0334930419921875,
|
| 314 |
+
"text": "γ",
|
| 315 |
+
"vowel": "i",
|
| 316 |
+
"vowel_length": 0.057893283665180206
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"consonant": "m",
|
| 320 |
+
"consonant_length": 0.058485276997089386,
|
| 321 |
+
"pitch": 6.074601173400879,
|
| 322 |
+
"text": "γ",
|
| 323 |
+
"vowel": "a",
|
| 324 |
+
"vowel_length": 0.09547590464353561
|
| 325 |
+
},
|
| 326 |
+
{
|
| 327 |
+
"consonant": "sh",
|
| 328 |
+
"consonant_length": 0.030789220705628395,
|
| 329 |
+
"pitch": 0.0,
|
| 330 |
+
"text": "γ·",
|
| 331 |
+
"vowel": "I",
|
| 332 |
+
"vowel_length": 0.06114967167377472
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"consonant": "t",
|
| 336 |
+
"consonant_length": 0.05993541330099106,
|
| 337 |
+
"pitch": 5.849719524383545,
|
| 338 |
+
"text": "γΏ",
|
| 339 |
+
"vowel": "a",
|
| 340 |
+
"vowel_length": 0.1888434886932373
|
| 341 |
+
}
|
| 342 |
+
],
|
| 343 |
+
"pause_mora": null
|
| 344 |
+
}
|
| 345 |
+
],
|
| 346 |
+
"intonationScale": 1.0,
|
| 347 |
+
"kana": "γγ§'γͺγ―/γ€'γ€/γ'γ³γγ_γΉγ«γ©γγ³γͺγ¨γ³γͺ'/γ΅γ³γ'/_γ·γγ«γ©'γγ«γ€γ’γγ'/γ€γγ'_γ·γΏ",
|
| 348 |
+
"outputSamplingRate": 24000,
|
| 349 |
+
"outputStereo": false,
|
| 350 |
+
"pitchScale": 0.0,
|
| 351 |
+
"postPhonemeLength": 0.10000000149011612,
|
| 352 |
+
"prePhonemeLength": 0.10000000149011612,
|
| 353 |
+
"speedScale": 1.0,
|
| 354 |
+
"volumeScale": 1.0
|
| 355 |
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"accent_phrases": [
|
| 3 |
+
{
|
| 4 |
+
"accent": 5,
|
| 5 |
+
"is_interrogative": false,
|
| 6 |
+
"moras": [
|
| 7 |
+
{
|
| 8 |
+
"consonant": "k",
|
| 9 |
+
"consonant_length": 0.09125985950231552,
|
| 10 |
+
"pitch": 5.689502716064453,
|
| 11 |
+
"text": "γ³",
|
| 12 |
+
"vowel": "o",
|
| 13 |
+
"vowel_length": 0.1464204043149948
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"consonant": null,
|
| 17 |
+
"consonant_length": null,
|
| 18 |
+
"pitch": 5.868537902832031,
|
| 19 |
+
"text": "γ³",
|
| 20 |
+
"vowel": "N",
|
| 21 |
+
"vowel_length": 0.07110694795846939
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"consonant": "n",
|
| 25 |
+
"consonant_length": 0.032809868454933167,
|
| 26 |
+
"pitch": 5.991527080535889,
|
| 27 |
+
"text": "γ",
|
| 28 |
+
"vowel": "i",
|
| 29 |
+
"vowel_length": 0.09959950298070908
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"consonant": "ch",
|
| 33 |
+
"consonant_length": 0.08417012542486191,
|
| 34 |
+
"pitch": 5.978631973266602,
|
| 35 |
+
"text": "γ",
|
| 36 |
+
"vowel": "i",
|
| 37 |
+
"vowel_length": 0.07654783874750137
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"consonant": "w",
|
| 41 |
+
"consonant_length": 0.07711347192525864,
|
| 42 |
+
"pitch": 6.040231227874756,
|
| 43 |
+
"text": "γ―",
|
| 44 |
+
"vowel": "a",
|
| 45 |
+
"vowel_length": 0.1825464963912964
|
| 46 |
+
}
|
| 47 |
+
],
|
| 48 |
+
"pause_mora": null
|
| 49 |
+
}
|
| 50 |
+
],
|
| 51 |
+
"intonationScale": 1.0,
|
| 52 |
+
"kana": "γ³γ³γγγ―'",
|
| 53 |
+
"outputSamplingRate": 24000,
|
| 54 |
+
"outputStereo": false,
|
| 55 |
+
"pitchScale": 0.0,
|
| 56 |
+
"postPhonemeLength": 0.10000000149011612,
|
| 57 |
+
"prePhonemeLength": 0.10000000149011612,
|
| 58 |
+
"speedScale": 1.0,
|
| 59 |
+
"volumeScale": 1.0
|
| 60 |
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"accent_phrases": [
|
| 3 |
+
{
|
| 4 |
+
"accent": 1,
|
| 5 |
+
"is_interrogative": false,
|
| 6 |
+
"moras": [
|
| 7 |
+
{
|
| 8 |
+
"consonant": "ky",
|
| 9 |
+
"consonant_length": 0.1269165426492691,
|
| 10 |
+
"pitch": 5.891244888305664,
|
| 11 |
+
"text": "γγ§",
|
| 12 |
+
"vowel": "o",
|
| 13 |
+
"vowel_length": 0.10178578644990921
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"consonant": null,
|
| 17 |
+
"consonant_length": null,
|
| 18 |
+
"pitch": 5.944635391235352,
|
| 19 |
+
"text": "γͺ",
|
| 20 |
+
"vowel": "o",
|
| 21 |
+
"vowel_length": 0.08867011219263077
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"consonant": "w",
|
| 25 |
+
"consonant_length": 0.06420903652906418,
|
| 26 |
+
"pitch": 5.655149459838867,
|
| 27 |
+
"text": "γ―",
|
| 28 |
+
"vowel": "a",
|
| 29 |
+
"vowel_length": 0.13292233645915985
|
| 30 |
+
}
|
| 31 |
+
],
|
| 32 |
+
"pause_mora": null
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"accent": 1,
|
| 36 |
+
"is_interrogative": false,
|
| 37 |
+
"moras": [
|
| 38 |
+
{
|
| 39 |
+
"consonant": null,
|
| 40 |
+
"consonant_length": null,
|
| 41 |
+
"pitch": 5.756601810455322,
|
| 42 |
+
"text": "γ€",
|
| 43 |
+
"vowel": "i",
|
| 44 |
+
"vowel_length": 0.09258951991796494
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"consonant": null,
|
| 48 |
+
"consonant_length": null,
|
| 49 |
+
"pitch": 6.103951930999756,
|
| 50 |
+
"text": "γ€",
|
| 51 |
+
"vowel": "i",
|
| 52 |
+
"vowel_length": 0.0974084809422493
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
"pause_mora": null
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"accent": 1,
|
| 59 |
+
"is_interrogative": false,
|
| 60 |
+
"moras": [
|
| 61 |
+
{
|
| 62 |
+
"consonant": "t",
|
| 63 |
+
"consonant_length": 0.0657225176692009,
|
| 64 |
+
"pitch": 6.139893531799316,
|
| 65 |
+
"text": "γ",
|
| 66 |
+
"vowel": "e",
|
| 67 |
+
"vowel_length": 0.1257137954235077
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"consonant": null,
|
| 71 |
+
"consonant_length": null,
|
| 72 |
+
"pitch": 6.0533294677734375,
|
| 73 |
+
"text": "γ³",
|
| 74 |
+
"vowel": "N",
|
| 75 |
+
"vowel_length": 0.07083306461572647
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"consonant": "k",
|
| 79 |
+
"consonant_length": 0.05886319279670715,
|
| 80 |
+
"pitch": 5.938107967376709,
|
| 81 |
+
"text": "γ",
|
| 82 |
+
"vowel": "i",
|
| 83 |
+
"vowel_length": 0.059421248733997345
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"consonant": "d",
|
| 87 |
+
"consonant_length": 0.05466592311859131,
|
| 88 |
+
"pitch": 5.8197479248046875,
|
| 89 |
+
"text": "γ",
|
| 90 |
+
"vowel": "e",
|
| 91 |
+
"vowel_length": 0.09957551956176758
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"consonant": "s",
|
| 95 |
+
"consonant_length": 0.03665236383676529,
|
| 96 |
+
"pitch": 0.0,
|
| 97 |
+
"text": "γΉ",
|
| 98 |
+
"vowel": "U",
|
| 99 |
+
"vowel_length": 0.06477490812540054
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"consonant": "k",
|
| 103 |
+
"consonant_length": 0.07497167587280273,
|
| 104 |
+
"pitch": 5.6952314376831055,
|
| 105 |
+
"text": "γ«",
|
| 106 |
+
"vowel": "a",
|
| 107 |
+
"vowel_length": 0.09379018098115921
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"consonant": "r",
|
| 111 |
+
"consonant_length": 0.03401697427034378,
|
| 112 |
+
"pitch": 5.5977702140808105,
|
| 113 |
+
"text": "γ©",
|
| 114 |
+
"vowel": "a",
|
| 115 |
+
"vowel_length": 0.14935533702373505
|
| 116 |
+
}
|
| 117 |
+
],
|
| 118 |
+
"pause_mora": {
|
| 119 |
+
"consonant": null,
|
| 120 |
+
"consonant_length": null,
|
| 121 |
+
"pitch": 0.0,
|
| 122 |
+
"text": "γ",
|
| 123 |
+
"vowel": "pau",
|
| 124 |
+
"vowel_length": 0.2590501606464386
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"accent": 5,
|
| 129 |
+
"is_interrogative": false,
|
| 130 |
+
"moras": [
|
| 131 |
+
{
|
| 132 |
+
"consonant": "k",
|
| 133 |
+
"consonant_length": 0.08955883234739304,
|
| 134 |
+
"pitch": 5.693568229675293,
|
| 135 |
+
"text": "γ³",
|
| 136 |
+
"vowel": "o",
|
| 137 |
+
"vowel_length": 0.09677395969629288
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"consonant": null,
|
| 141 |
+
"consonant_length": null,
|
| 142 |
+
"pitch": 5.938162803649902,
|
| 143 |
+
"text": "γͺ",
|
| 144 |
+
"vowel": "o",
|
| 145 |
+
"vowel_length": 0.12056692689657211
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"consonant": null,
|
| 149 |
+
"consonant_length": null,
|
| 150 |
+
"pitch": 6.016718864440918,
|
| 151 |
+
"text": "γ¨",
|
| 152 |
+
"vowel": "e",
|
| 153 |
+
"vowel_length": 0.147250235080719
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"consonant": null,
|
| 157 |
+
"consonant_length": null,
|
| 158 |
+
"pitch": 6.010623931884766,
|
| 159 |
+
"text": "γ³",
|
| 160 |
+
"vowel": "N",
|
| 161 |
+
"vowel_length": 0.08805551379919052
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"consonant": null,
|
| 165 |
+
"consonant_length": null,
|
| 166 |
+
"pitch": 6.007989883422852,
|
| 167 |
+
"text": "γͺ",
|
| 168 |
+
"vowel": "o",
|
| 169 |
+
"vowel_length": 0.10105019062757492
|
| 170 |
+
}
|
| 171 |
+
],
|
| 172 |
+
"pause_mora": null
|
| 173 |
+
},
|
| 174 |
+
{
|
| 175 |
+
"accent": 3,
|
| 176 |
+
"is_interrogative": false,
|
| 177 |
+
"moras": [
|
| 178 |
+
{
|
| 179 |
+
"consonant": "s",
|
| 180 |
+
"consonant_length": 0.0925176665186882,
|
| 181 |
+
"pitch": 6.003979206085205,
|
| 182 |
+
"text": "γ΅",
|
| 183 |
+
"vowel": "a",
|
| 184 |
+
"vowel_length": 0.1288621425628662
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"consonant": null,
|
| 188 |
+
"consonant_length": null,
|
| 189 |
+
"pitch": 6.026742935180664,
|
| 190 |
+
"text": "γ³",
|
| 191 |
+
"vowel": "N",
|
| 192 |
+
"vowel_length": 0.0830387994647026
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"consonant": "p",
|
| 196 |
+
"consonant_length": 0.054095521569252014,
|
| 197 |
+
"pitch": 6.099804878234863,
|
| 198 |
+
"text": "γ",
|
| 199 |
+
"vowel": "o",
|
| 200 |
+
"vowel_length": 0.1004747673869133
|
| 201 |
+
}
|
| 202 |
+
],
|
| 203 |
+
"pause_mora": null
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"accent": 4,
|
| 207 |
+
"is_interrogative": false,
|
| 208 |
+
"moras": [
|
| 209 |
+
{
|
| 210 |
+
"consonant": "sh",
|
| 211 |
+
"consonant_length": 0.021739566698670387,
|
| 212 |
+
"pitch": 0.0,
|
| 213 |
+
"text": "γ·",
|
| 214 |
+
"vowel": "I",
|
| 215 |
+
"vowel_length": 0.04718482494354248
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"consonant": "t",
|
| 219 |
+
"consonant_length": 0.05912453681230545,
|
| 220 |
+
"pitch": 6.083391189575195,
|
| 221 |
+
"text": "γ",
|
| 222 |
+
"vowel": "e",
|
| 223 |
+
"vowel_length": 0.08641223609447479
|
| 224 |
+
},
|
| 225 |
+
{
|
| 226 |
+
"consonant": "k",
|
| 227 |
+
"consonant_length": 0.05805716663599014,
|
| 228 |
+
"pitch": 6.061047554016113,
|
| 229 |
+
"text": "γ«",
|
| 230 |
+
"vowel": "a",
|
| 231 |
+
"vowel_length": 0.0868045762181282
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"consonant": "r",
|
| 235 |
+
"consonant_length": 0.03226054459810257,
|
| 236 |
+
"pitch": 5.876008987426758,
|
| 237 |
+
"text": "γ©",
|
| 238 |
+
"vowel": "a",
|
| 239 |
+
"vowel_length": 0.14383982121944427
|
| 240 |
+
}
|
| 241 |
+
],
|
| 242 |
+
"pause_mora": {
|
| 243 |
+
"consonant": null,
|
| 244 |
+
"consonant_length": null,
|
| 245 |
+
"pitch": 0.0,
|
| 246 |
+
"text": "γ",
|
| 247 |
+
"vowel": "pau",
|
| 248 |
+
"vowel_length": 0.2888171374797821
|
| 249 |
+
}
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"accent": 5,
|
| 253 |
+
"is_interrogative": false,
|
| 254 |
+
"moras": [
|
| 255 |
+
{
|
| 256 |
+
"consonant": "k",
|
| 257 |
+
"consonant_length": 0.0854499563574791,
|
| 258 |
+
"pitch": 5.536592483520508,
|
| 259 |
+
"text": "γ«",
|
| 260 |
+
"vowel": "a",
|
| 261 |
+
"vowel_length": 0.12839564681053162
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"consonant": null,
|
| 265 |
+
"consonant_length": null,
|
| 266 |
+
"pitch": 5.821318626403809,
|
| 267 |
+
"text": "γ€",
|
| 268 |
+
"vowel": "i",
|
| 269 |
+
"vowel_length": 0.07545183598995209
|
| 270 |
+
},
|
| 271 |
+
{
|
| 272 |
+
"consonant": "m",
|
| 273 |
+
"consonant_length": 0.05912942439317703,
|
| 274 |
+
"pitch": 6.023046493530273,
|
| 275 |
+
"text": "γ’",
|
| 276 |
+
"vowel": "o",
|
| 277 |
+
"vowel_length": 0.0851563960313797
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
"consonant": "n",
|
| 281 |
+
"consonant_length": 0.05413999408483505,
|
| 282 |
+
"pitch": 6.078396797180176,
|
| 283 |
+
"text": "γ",
|
| 284 |
+
"vowel": "o",
|
| 285 |
+
"vowel_length": 0.08871601521968842
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"consonant": "n",
|
| 289 |
+
"consonant_length": 0.07090981304645538,
|
| 290 |
+
"pitch": 6.0716552734375,
|
| 291 |
+
"text": "γ",
|
| 292 |
+
"vowel": "i",
|
| 293 |
+
"vowel_length": 0.0862032100558281
|
| 294 |
+
}
|
| 295 |
+
],
|
| 296 |
+
"pause_mora": null
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"accent": 3,
|
| 300 |
+
"is_interrogative": false,
|
| 301 |
+
"moras": [
|
| 302 |
+
{
|
| 303 |
+
"consonant": null,
|
| 304 |
+
"consonant_length": null,
|
| 305 |
+
"pitch": 6.005620956420898,
|
| 306 |
+
"text": "γ€",
|
| 307 |
+
"vowel": "i",
|
| 308 |
+
"vowel_length": 0.09876994043588638
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
"consonant": "k",
|
| 312 |
+
"consonant_length": 0.07177173346281052,
|
| 313 |
+
"pitch": 6.0334930419921875,
|
| 314 |
+
"text": "γ",
|
| 315 |
+
"vowel": "i",
|
| 316 |
+
"vowel_length": 0.057893283665180206
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"consonant": "m",
|
| 320 |
+
"consonant_length": 0.058485276997089386,
|
| 321 |
+
"pitch": 6.074601173400879,
|
| 322 |
+
"text": "γ",
|
| 323 |
+
"vowel": "a",
|
| 324 |
+
"vowel_length": 0.09547590464353561
|
| 325 |
+
},
|
| 326 |
+
{
|
| 327 |
+
"consonant": "sh",
|
| 328 |
+
"consonant_length": 0.030789220705628395,
|
| 329 |
+
"pitch": 0.0,
|
| 330 |
+
"text": "γ·",
|
| 331 |
+
"vowel": "I",
|
| 332 |
+
"vowel_length": 0.06114967167377472
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"consonant": "t",
|
| 336 |
+
"consonant_length": 0.05993541330099106,
|
| 337 |
+
"pitch": 5.849719524383545,
|
| 338 |
+
"text": "γΏ",
|
| 339 |
+
"vowel": "a",
|
| 340 |
+
"vowel_length": 0.1888434886932373
|
| 341 |
+
}
|
| 342 |
+
],
|
| 343 |
+
"pause_mora": null
|
| 344 |
+
}
|
| 345 |
+
],
|
| 346 |
+
"intonationScale": 1.0,
|
| 347 |
+
"kana": "γγ§'γͺγ―/γ€'γ€/γ'γ³γγ_γΉγ«γ©γγ³γͺγ¨γ³γͺ'/γ΅γ³γ'/_γ·γγ«γ©'γγ«γ€γ’γγ'/γ€γγ'_γ·γΏ",
|
| 348 |
+
"outputSamplingRate": 24000,
|
| 349 |
+
"outputStereo": false,
|
| 350 |
+
"pitchScale": 0.0,
|
| 351 |
+
"postPhonemeLength": 0.10000000149011612,
|
| 352 |
+
"prePhonemeLength": 0.10000000149011612,
|
| 353 |
+
"speedScale": 0.75,
|
| 354 |
+
"volumeScale": 1.0
|
| 355 |
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Regenerate the committed VOICEVOX synthesis fixtures.
|
| 2 |
+
|
| 3 |
+
uv run --extra voice python tests/fixtures/make_synth_fixtures.py
|
| 4 |
+
|
| 5 |
+
This is a **maintainer action**, not part of CI. The outputs are the ground truth for every
|
| 6 |
+
AVTR-02 assertion in plan 01-06, so every duration here is read from the WAV header and never
|
| 7 |
+
summed from the query - a summed query is the very thing the no-drift test exists to check.
|
| 8 |
+
|
| 9 |
+
Three cases, chosen deliberately:
|
| 10 |
+
|
| 11 |
+
===== ======================= ===== ==============================================================
|
| 12 |
+
Case Text Speed Why this text
|
| 13 |
+
===== ======================= ===== ==============================================================
|
| 14 |
+
short γγγ«γ‘γ― 1.0 Contains ``N`` (γ) between vowels - exercises the closed-mouth
|
| 15 |
+
path. Doubles as the push-to-talk fixture for VOIC-02.
|
| 16 |
+
long (see LONG_TEXT) 1.0 γ§γ devoices to ``d e s U`` and γγ to ``sh I t a``, so the
|
| 17 |
+
fixture exercises the uppercase devoiced vowels that a
|
| 18 |
+
lowercase-only lookup silently drops. ~36 moras, long enough
|
| 19 |
+
that float-accumulation drift would be plainly visible.
|
| 20 |
+
slow same as long 0.75 VOIC-03's mechanism; the timeline must be exactly 1/0.75x
|
| 21 |
+
longer, pre/post silence included.
|
| 22 |
+
===== ======================= ===== ==============================================================
|
| 23 |
+
|
| 24 |
+
The generator refuses to write a ``long`` fixture that does not actually contain a devoiced vowel,
|
| 25 |
+
a pause mora and 20+ moras. A fixture that does not exercise those branches is the wrong fixture,
|
| 26 |
+
and finding that out here is much cheaper than finding it out in plan 01-06.
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
from __future__ import annotations
|
| 30 |
+
|
| 31 |
+
import datetime as dt
|
| 32 |
+
import json
|
| 33 |
+
import sys
|
| 34 |
+
import wave
|
| 35 |
+
from pathlib import Path
|
| 36 |
+
|
| 37 |
+
FIXTURES = Path(__file__).resolve().parent
|
| 38 |
+
REPO_ROOT = FIXTURES.parents[1]
|
| 39 |
+
sys.path.insert(0, str(REPO_ROOT / "src"))
|
| 40 |
+
|
| 41 |
+
SHORT_TEXT = "γγγ«γ‘γ―"
|
| 42 |
+
LONG_TEXT = "δ»ζ₯γ―γγ倩ζ°γ§γγγγε
¬εγζ£ζ©γγ¦γγγθ²·γη©γ«θ‘γγΎγγγ"
|
| 43 |
+
|
| 44 |
+
VOICEVOX_CORE_VERSION = "0.17.0"
|
| 45 |
+
VOICEVOX_VVM_VERSION = "0.17.0"
|
| 46 |
+
|
| 47 |
+
DEVOICED_VOWELS = frozenset("AIUEO")
|
| 48 |
+
MIN_LONG_MORAS = 20
|
| 49 |
+
|
| 50 |
+
#: 24000 Hz / 256 samples. Every phoneme is quantised to a whole number of these.
|
| 51 |
+
FRAMERATE = 93.75
|
| 52 |
+
|
| 53 |
+
CASES = [
|
| 54 |
+
("short", SHORT_TEXT, 1.0, "speech_ja.wav"),
|
| 55 |
+
("long", LONG_TEXT, 1.0, "speech_ja_long.wav"),
|
| 56 |
+
("slow", LONG_TEXT, 0.75, "speech_ja_slow.wav"),
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def flatten_moras(query: dict) -> list[dict]:
|
| 61 |
+
"""Every mora in utterance order, each accent phrase's pause mora following its moras."""
|
| 62 |
+
moras: list[dict] = []
|
| 63 |
+
for phrase in query["accent_phrases"]:
|
| 64 |
+
moras.extend(phrase["moras"])
|
| 65 |
+
if phrase["pause_mora"]:
|
| 66 |
+
moras.append(phrase["pause_mora"])
|
| 67 |
+
return moras
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def phoneme_lengths(query: dict) -> list[float]:
|
| 71 |
+
"""Unscaled phoneme durations in seconds, wrapped in the pre/post silence, in order."""
|
| 72 |
+
silence = {"vowel": "pau", "consonant": None, "consonant_length": None}
|
| 73 |
+
moras = [
|
| 74 |
+
{**silence, "vowel_length": query["prePhonemeLength"]},
|
| 75 |
+
*flatten_moras(query),
|
| 76 |
+
{**silence, "vowel_length": query["postPhonemeLength"]},
|
| 77 |
+
]
|
| 78 |
+
lengths: list[float] = []
|
| 79 |
+
for mora in moras:
|
| 80 |
+
if mora["consonant"] is not None:
|
| 81 |
+
lengths.append(mora["consonant_length"])
|
| 82 |
+
lengths.append(mora["vowel_length"])
|
| 83 |
+
return lengths
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def write_wav(path: Path, wav_bytes: bytes) -> tuple[float, int]:
|
| 87 |
+
"""Write the WAV; return its true duration and frame count from the header just written.
|
| 88 |
+
|
| 89 |
+
``frame_count`` is in VOICEVOX frames (256 samples at 24000 Hz = 93.75 fps), not PCM samples,
|
| 90 |
+
because that is the unit plan 01-06's +/-1 frame no-drift tolerance is expressed in.
|
| 91 |
+
"""
|
| 92 |
+
path.write_bytes(wav_bytes)
|
| 93 |
+
with wave.open(str(path), "rb") as handle:
|
| 94 |
+
assert handle.getframerate() == 24000, handle.getframerate()
|
| 95 |
+
assert handle.getnchannels() == 1, handle.getnchannels()
|
| 96 |
+
assert handle.getsampwidth() == 2, handle.getsampwidth()
|
| 97 |
+
samples = handle.getnframes()
|
| 98 |
+
assert samples % 256 == 0, samples
|
| 99 |
+
return samples / float(handle.getframerate()), samples // 256
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def main() -> int:
|
| 103 |
+
try:
|
| 104 |
+
import voicevox_core # noqa: F401
|
| 105 |
+
except ImportError:
|
| 106 |
+
print(
|
| 107 |
+
"voicevox_core is not installed, so the fixtures cannot be regenerated.\n"
|
| 108 |
+
"Install it with `uv sync --extra dev --extra voice`; see docs/VOICEVOX-SETUP.md.\n"
|
| 109 |
+
"Regeneration is a maintainer action - CI does not need it.",
|
| 110 |
+
file=sys.stderr,
|
| 111 |
+
)
|
| 112 |
+
return 1
|
| 113 |
+
|
| 114 |
+
from japanese_avatar.voice.tts import SPEAKER_STYLE_ID, synthesize
|
| 115 |
+
|
| 116 |
+
meta: dict = {
|
| 117 |
+
"generated": dt.datetime.now(dt.UTC).date().isoformat(),
|
| 118 |
+
"voicevox_core": VOICEVOX_CORE_VERSION,
|
| 119 |
+
"voicevox_vvm": VOICEVOX_VVM_VERSION,
|
| 120 |
+
"style_id": SPEAKER_STYLE_ID,
|
| 121 |
+
"cases": {},
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
for case, text, speed, wav_name in CASES:
|
| 125 |
+
result = synthesize(text, speed=speed)
|
| 126 |
+
query = result.audio_query
|
| 127 |
+
|
| 128 |
+
query_name = f"audio_query_{case}.json"
|
| 129 |
+
(FIXTURES / query_name).write_text(
|
| 130 |
+
json.dumps(query, indent=2, ensure_ascii=False, sort_keys=True) + "\n",
|
| 131 |
+
encoding="utf-8",
|
| 132 |
+
newline="\n",
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
duration, frame_count = write_wav(FIXTURES / wav_name, result.wav_bytes)
|
| 136 |
+
# The WAV we just wrote must agree with what synthesize() reported, bit for bit.
|
| 137 |
+
assert duration == result.duration, (case, duration, result.duration)
|
| 138 |
+
|
| 139 |
+
# Ground truth for plan 01-06. See the "Frame quantisation" section of
|
| 140 |
+
# docs/VOICEVOX-SETUP.md: quantise at speed 1.0 FIRST, then divide the frame count by
|
| 141 |
+
# speedScale and round again. Dividing the length by speedScale before quantising is a
|
| 142 |
+
# different, wrong answer that only coincides at speed 1.0.
|
| 143 |
+
predicted = sum(
|
| 144 |
+
round(round(length * FRAMERATE) / speed) for length in phoneme_lengths(query)
|
| 145 |
+
)
|
| 146 |
+
assert predicted == frame_count, (case, predicted, frame_count)
|
| 147 |
+
|
| 148 |
+
moras = flatten_moras(query)
|
| 149 |
+
vowels = [m["vowel"] for m in moras]
|
| 150 |
+
pause_moras = sum(1 for p in query["accent_phrases"] if p["pause_mora"])
|
| 151 |
+
|
| 152 |
+
print(
|
| 153 |
+
f"{case:>5}: moras={len(moras):<3} pause_moras={pause_moras} frames={frame_count:<5} "
|
| 154 |
+
f"duration={duration!r} vowels={sorted(set(vowels))}"
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
if case == "long":
|
| 158 |
+
if len(moras) < MIN_LONG_MORAS:
|
| 159 |
+
print(
|
| 160 |
+
f"FAIL: the long fixture has {len(moras)} moras, fewer than "
|
| 161 |
+
f"{MIN_LONG_MORAS}. Pick a longer sentence and record which one.",
|
| 162 |
+
file=sys.stderr,
|
| 163 |
+
)
|
| 164 |
+
return 1
|
| 165 |
+
devoiced = sorted(set(vowels) & DEVOICED_VOWELS)
|
| 166 |
+
if not devoiced:
|
| 167 |
+
print(
|
| 168 |
+
"FAIL: the long fixture contains no devoiced vowel (A/I/U/E/O). Its entire "
|
| 169 |
+
"job is to exercise that branch - pick a different sentence.",
|
| 170 |
+
file=sys.stderr,
|
| 171 |
+
)
|
| 172 |
+
return 1
|
| 173 |
+
if not pause_moras:
|
| 174 |
+
print(
|
| 175 |
+
"FAIL: the long fixture produced no pause_mora. Pick a sentence with a "
|
| 176 |
+
"comma so the pause-ordering branch is covered.",
|
| 177 |
+
file=sys.stderr,
|
| 178 |
+
)
|
| 179 |
+
return 1
|
| 180 |
+
print(f" devoiced vowels present: {devoiced}")
|
| 181 |
+
|
| 182 |
+
meta["cases"][case] = {
|
| 183 |
+
"text": text,
|
| 184 |
+
"speed_scale": speed,
|
| 185 |
+
"query": query_name,
|
| 186 |
+
"wav": wav_name,
|
| 187 |
+
"duration_seconds": duration,
|
| 188 |
+
"frame_count": frame_count,
|
| 189 |
+
"mora_count": len(moras),
|
| 190 |
+
"pause_mora_count": pause_moras,
|
| 191 |
+
"vowel_symbols": vowels,
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
# Not exactly 1/0.75: VOICEVOX re-quantises every phoneme after scaling, so the realised
|
| 195 |
+
# ratio lands within a fraction of a percent of it rather than on it. 2% is the tolerance
|
| 196 |
+
# the fixture contract is written against.
|
| 197 |
+
ratio = meta["cases"]["slow"]["duration_seconds"] / meta["cases"]["long"]["duration_seconds"]
|
| 198 |
+
print(f"slow/long duration ratio = {ratio:.6f} (expected ~{1 / 0.75:.6f})")
|
| 199 |
+
if abs(ratio - 1 / 0.75) >= 0.02:
|
| 200 |
+
print(
|
| 201 |
+
f"FAIL: speedScale=0.75 did not lengthen the audio by ~1/0.75x: {ratio}",
|
| 202 |
+
file=sys.stderr,
|
| 203 |
+
)
|
| 204 |
+
return 1
|
| 205 |
+
|
| 206 |
+
(FIXTURES / "synth_meta.json").write_text(
|
| 207 |
+
json.dumps(meta, indent=2, ensure_ascii=False, sort_keys=True) + "\n",
|
| 208 |
+
encoding="utf-8",
|
| 209 |
+
# Explicit LF: the default translates to CRLF on Windows, which would make the committed
|
| 210 |
+
# fixtures differ byte-for-byte depending on which OS last regenerated them.
|
| 211 |
+
newline="\n",
|
| 212 |
+
)
|
| 213 |
+
print(f"wrote {FIXTURES / 'synth_meta.json'}")
|
| 214 |
+
return 0
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
if __name__ == "__main__":
|
| 218 |
+
raise SystemExit(main())
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:726ee8497999a5aac84567edbc53fb6751529348520bb1643c5a98a548e9f1fc
|
| 3 |
+
size 50732
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ccbd3f1d0cd7714e1d7ed12518978087be6c251992c9c5c59600742d45291160
|
| 3 |
+
size 264236
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c91341fd06952c7e2bab243dffc77cfa4d1db36c636ddc0e74999b309238d544
|
| 3 |
+
size 354348
|
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cases": {
|
| 3 |
+
"long": {
|
| 4 |
+
"duration_seconds": 5.504,
|
| 5 |
+
"frame_count": 516,
|
| 6 |
+
"mora_count": 36,
|
| 7 |
+
"pause_mora_count": 2,
|
| 8 |
+
"query": "audio_query_long.json",
|
| 9 |
+
"speed_scale": 1.0,
|
| 10 |
+
"text": "δ»ζ₯γ―γγ倩ζ°γ§γγγγε
¬εγζ£ζ©γγ¦γγγθ²·γη©γ«θ‘γγΎγγγ",
|
| 11 |
+
"vowel_symbols": [
|
| 12 |
+
"o",
|
| 13 |
+
"o",
|
| 14 |
+
"a",
|
| 15 |
+
"i",
|
| 16 |
+
"i",
|
| 17 |
+
"e",
|
| 18 |
+
"N",
|
| 19 |
+
"i",
|
| 20 |
+
"e",
|
| 21 |
+
"U",
|
| 22 |
+
"a",
|
| 23 |
+
"a",
|
| 24 |
+
"pau",
|
| 25 |
+
"o",
|
| 26 |
+
"o",
|
| 27 |
+
"e",
|
| 28 |
+
"N",
|
| 29 |
+
"o",
|
| 30 |
+
"a",
|
| 31 |
+
"N",
|
| 32 |
+
"o",
|
| 33 |
+
"I",
|
| 34 |
+
"e",
|
| 35 |
+
"a",
|
| 36 |
+
"a",
|
| 37 |
+
"pau",
|
| 38 |
+
"a",
|
| 39 |
+
"i",
|
| 40 |
+
"o",
|
| 41 |
+
"o",
|
| 42 |
+
"i",
|
| 43 |
+
"i",
|
| 44 |
+
"i",
|
| 45 |
+
"a",
|
| 46 |
+
"I",
|
| 47 |
+
"a"
|
| 48 |
+
],
|
| 49 |
+
"wav": "speech_ja_long.wav"
|
| 50 |
+
},
|
| 51 |
+
"short": {
|
| 52 |
+
"duration_seconds": 1.056,
|
| 53 |
+
"frame_count": 99,
|
| 54 |
+
"mora_count": 5,
|
| 55 |
+
"pause_mora_count": 0,
|
| 56 |
+
"query": "audio_query_short.json",
|
| 57 |
+
"speed_scale": 1.0,
|
| 58 |
+
"text": "γγγ«γ‘γ―",
|
| 59 |
+
"vowel_symbols": [
|
| 60 |
+
"o",
|
| 61 |
+
"N",
|
| 62 |
+
"i",
|
| 63 |
+
"i",
|
| 64 |
+
"a"
|
| 65 |
+
],
|
| 66 |
+
"wav": "speech_ja.wav"
|
| 67 |
+
},
|
| 68 |
+
"slow": {
|
| 69 |
+
"duration_seconds": 7.381333333333333,
|
| 70 |
+
"frame_count": 692,
|
| 71 |
+
"mora_count": 36,
|
| 72 |
+
"pause_mora_count": 2,
|
| 73 |
+
"query": "audio_query_slow.json",
|
| 74 |
+
"speed_scale": 0.75,
|
| 75 |
+
"text": "δ»ζ₯γ―γγ倩ζ°γ§γγγγε
¬εγζ£ζ©γγ¦γγγθ²·γη©γ«θ‘γγΎγγγ",
|
| 76 |
+
"vowel_symbols": [
|
| 77 |
+
"o",
|
| 78 |
+
"o",
|
| 79 |
+
"a",
|
| 80 |
+
"i",
|
| 81 |
+
"i",
|
| 82 |
+
"e",
|
| 83 |
+
"N",
|
| 84 |
+
"i",
|
| 85 |
+
"e",
|
| 86 |
+
"U",
|
| 87 |
+
"a",
|
| 88 |
+
"a",
|
| 89 |
+
"pau",
|
| 90 |
+
"o",
|
| 91 |
+
"o",
|
| 92 |
+
"e",
|
| 93 |
+
"N",
|
| 94 |
+
"o",
|
| 95 |
+
"a",
|
| 96 |
+
"N",
|
| 97 |
+
"o",
|
| 98 |
+
"I",
|
| 99 |
+
"e",
|
| 100 |
+
"a",
|
| 101 |
+
"a",
|
| 102 |
+
"pau",
|
| 103 |
+
"a",
|
| 104 |
+
"i",
|
| 105 |
+
"o",
|
| 106 |
+
"o",
|
| 107 |
+
"i",
|
| 108 |
+
"i",
|
| 109 |
+
"i",
|
| 110 |
+
"a",
|
| 111 |
+
"I",
|
| 112 |
+
"a"
|
| 113 |
+
],
|
| 114 |
+
"wav": "speech_ja_slow.wav"
|
| 115 |
+
}
|
| 116 |
+
},
|
| 117 |
+
"generated": "2026-08-27",
|
| 118 |
+
"style_id": 3,
|
| 119 |
+
"voicevox_core": "0.17.0",
|
| 120 |
+
"voicevox_vvm": "0.17.0"
|
| 121 |
+
}
|
|
@@ -23,8 +23,27 @@ LONG_TEXT = "δ»ζ₯γ―γγ倩ζ°γ§γγγγε
¬εγζ£ζ©γγ¦γγγ
|
|
| 23 |
|
| 24 |
|
| 25 |
@pytest.fixture(scope="module")
|
| 26 |
-
def
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
def test_synthesize_returns_nonempty_wav(short_result):
|
|
@@ -49,13 +68,13 @@ def test_audio_query_is_plain_json_roundtrippable(short_result):
|
|
| 49 |
assert key in query, key
|
| 50 |
|
| 51 |
|
| 52 |
-
def test_moras_carry_timings(
|
| 53 |
"""Per-mora timings exist, and consonant/consonant_length are genuinely optional.
|
| 54 |
|
| 55 |
The optionality is asserted rather than assumed: plan 01-06's timeline builder must not index
|
| 56 |
into a key that is absent for every vowel-only mora (γ’) and for every pause.
|
| 57 |
"""
|
| 58 |
-
result =
|
| 59 |
moras = []
|
| 60 |
for phrase in result.audio_query["accent_phrases"]:
|
| 61 |
moras.extend(phrase["moras"])
|
|
@@ -74,9 +93,9 @@ def test_moras_carry_timings(short_result):
|
|
| 74 |
assert without_consonant, "expected at least one mora with no consonant at all"
|
| 75 |
|
| 76 |
|
| 77 |
-
def test_speed_scale_lengthens_audio():
|
| 78 |
-
normal =
|
| 79 |
-
slow =
|
| 80 |
assert slow.duration > normal.duration
|
| 81 |
ratio = slow.duration / normal.duration
|
| 82 |
assert abs(ratio - 1 / 0.75) < 0.02 * (1 / 0.75), ratio
|
|
@@ -89,6 +108,18 @@ def test_output_sampling_rate_is_24000(short_result):
|
|
| 89 |
assert SPEAKER_STYLE_ID == 3
|
| 90 |
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
def test_no_gpu_imports_on_synthesis_path():
|
| 93 |
modules = sorted(VOICE_PKG.glob("*.py"))
|
| 94 |
assert modules, f"no modules found under {VOICE_PKG}"
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
@pytest.fixture(scope="module")
|
| 26 |
+
def synth():
|
| 27 |
+
"""Memoised synthesis, keyed by (text, speed).
|
| 28 |
+
|
| 29 |
+
The engine is deterministic, so synthesising the same pair twice is pure waste - and this
|
| 30 |
+
module would otherwise do it six times over, pushing the quick loop past the 15 s feedback
|
| 31 |
+
budget in ``01-VALIDATION.md``. Three distinct syntheses cover every test below.
|
| 32 |
+
"""
|
| 33 |
+
cache: dict[tuple[str, float], object] = {}
|
| 34 |
+
|
| 35 |
+
def _synth(text: str, speed: float = 1.0):
|
| 36 |
+
key = (text, speed)
|
| 37 |
+
if key not in cache:
|
| 38 |
+
cache[key] = synthesize(text, speed=speed)
|
| 39 |
+
return cache[key]
|
| 40 |
+
|
| 41 |
+
return _synth
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@pytest.fixture(scope="module")
|
| 45 |
+
def short_result(synth):
|
| 46 |
+
return synth(SHORT_TEXT)
|
| 47 |
|
| 48 |
|
| 49 |
def test_synthesize_returns_nonempty_wav(short_result):
|
|
|
|
| 68 |
assert key in query, key
|
| 69 |
|
| 70 |
|
| 71 |
+
def test_moras_carry_timings(synth):
|
| 72 |
"""Per-mora timings exist, and consonant/consonant_length are genuinely optional.
|
| 73 |
|
| 74 |
The optionality is asserted rather than assumed: plan 01-06's timeline builder must not index
|
| 75 |
into a key that is absent for every vowel-only mora (γ’) and for every pause.
|
| 76 |
"""
|
| 77 |
+
result = synth(LONG_TEXT)
|
| 78 |
moras = []
|
| 79 |
for phrase in result.audio_query["accent_phrases"]:
|
| 80 |
moras.extend(phrase["moras"])
|
|
|
|
| 93 |
assert without_consonant, "expected at least one mora with no consonant at all"
|
| 94 |
|
| 95 |
|
| 96 |
+
def test_speed_scale_lengthens_audio(synth):
|
| 97 |
+
normal = synth(LONG_TEXT, speed=1.0)
|
| 98 |
+
slow = synth(LONG_TEXT, speed=0.75)
|
| 99 |
assert slow.duration > normal.duration
|
| 100 |
ratio = slow.duration / normal.duration
|
| 101 |
assert abs(ratio - 1 / 0.75) < 0.02 * (1 / 0.75), ratio
|
|
|
|
| 108 |
assert SPEAKER_STYLE_ID == 3
|
| 109 |
|
| 110 |
|
| 111 |
+
def test_fixtures_match_current_engine(synth_meta, synth):
|
| 112 |
+
"""Regression guard: if a voicevox_core bump changes timings, this fails loudly
|
| 113 |
+
rather than test_golden_timeline failing mysteriously in plan 01-06."""
|
| 114 |
+
for case, meta in synth_meta["cases"].items():
|
| 115 |
+
r = synth(meta["text"], speed=meta["speed_scale"])
|
| 116 |
+
assert abs(r.duration - meta["duration_seconds"]) < 0.011, (
|
| 117 |
+
f"{case}: engine now produces {r.duration}s vs recorded {meta['duration_seconds']}s. "
|
| 118 |
+
"Regenerate with tests/fixtures/make_synth_fixtures.py and re-review the "
|
| 119 |
+
"golden timeline."
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
def test_no_gpu_imports_on_synthesis_path():
|
| 124 |
modules = sorted(VOICE_PKG.glob("*.py"))
|
| 125 |
assert modules, f"no modules found under {VOICE_PKG}"
|