Releases: ayutaz/sanoTTS-jp
Release list
v0.3.0 — 実機で漢字を喋り、実時間の要件を満たした
v0.3.0 — 実機で漢字を喋り、実時間の要件を満たした
2026-09-03。モデルの重みは v0.1.0 から 1 bit も変わっていません(v3 = Stage 3 を 80,000 step)。
変わったのは端末側のコードと配布物です。
何が変わったか
1. スタックチャン(M5Stack CoreS3)で漢字かな交じり文を喋ります
ESP32-S3 の上だけで G2P が完結します。 ホスト側の前処理は要りません。
辞書 13.7 MB を flash から読み、形態素解析(Viterbi)→ アクセント規則 → NJD の各段 →
フルコンテキストラベル → 音素 ID まで、すべて端末の C 実装です。
! の印は要らなくなりました。 行をそのまま打つと、端末が 3 通りに振り分けます。
| 打った行 | 経路 |
|---|---|
今日は良い天気ですね。 |
辞書(漢字経路) |
コンニチハ |
辞書 |
きょ][おわよ][いて][んきです°ね |
かな(中間表現) |
きょ][おわよ][んきです°ね。(中間表現に 。 が混ざる) |
位置つきで拒否(黙って別経路に回さない) |
判定はかな G2P のトークナイザそのもので、ホスト側の scripts/to_intermediate.py と
held-out 298 文 + 中間表現 298 行で 596/596 一致します。
2. 実時間の要件(RTF ≤ 0.5)を満たしました
| v0.2.0 相当(9/2 夜) | v0.3.0 | |
|---|---|---|
| 満チャンク 1 pull の xRT | 0.926 | 0.446 |
| 1 step | 18,378,513 cyc | 11,659,500 cyc |
| アンダーラン | 1 / 14 | 0 |
| 鳴らし始めまで | 719 ms | 384 ms |
| 内部 RAM の空き(起動直後) | 99,987 B | 132,039 B |
| arena(静的確保) | 212,992 B | 180,224 B |
出力の波形は 1 bit も変わっていません(checksum 0xa69a7ebbb5ccb05f が 9/2 から同一)。
速くなったのは、捨てられる出力を計算しない・token を持ち越す・GELU のコード生成を直す・
D-cache の行を 64 B にする、といった出力を変えない変更の積み重ねです。
3. 音を聴きました
これまでの一致はすべて「OpenJTalk と同じ出力か」という機械的な照合で、
日本語として通用するかは誰も確かめていませんでした。実機の音を聴いて確認しました。
**「品質を測った」ではなく「破綻していないことを人が 1 度確かめた」**と読んでください。
⚠️ v0.2.0 から必ず入れ替えてください
配布していた int8 の重みを、現行コードが拒みます
v0.2.0 の saanotts-jp-v3-int8.bin は blob v1(643,936 B)で、
現行コードは起動時に SAAN_ERR_VERSION で止まります。v0.3.0 は v2(654,032 B)です。
blob v2 は int8 の conv 重みを [cout][k][align16(cin)] に 0 埋めして並べ直したもので、
PIE 命令に転置コピー無しで渡せるようにしたものです(+10,096 B)。数値は同じです。
配布していた firmware が、native USB だけの板で操作できませんでした
v0.1.1 / v0.2.0 のイメージはコンソール入力が UART0 でした。CoreS3 や AtomS3 は
USB-シリアル変換を持たないので、起動はするのに かな> に何を打っても届きません。
v0.3.0 のイメージは USB Serial/JTAG 入力です。
配布物
| 資産 | 大きさ | 何に使うか |
|---|---|---|
m5-cores3-firmware-kanji-16mb.bin |
16 MB | M5Stack CoreS3 / スタックチャン。焼くだけ。漢字 + スピーカー + 画面 |
esp32s3-firmware-kanji-16mb-usbjtag.bin |
16 MB | ESP32-S3 DevKit(16 MB flash)。漢字。I2S DAC で発声 |
esp32s3-firmware-w8a8-pie-usbjtag.bin |
8 MB | 同(8 MB flash)。かな中間表現のみ(辞書なし) |
saanotts-jp-v3-int8.bin |
654,032 B | 端末用の重み。blob v2 |
saanotts-jp-v3-fp32.bin |
2,249,792 B | ホストで動かす / ゴールデンテスト用 |
golden-v3-int8.bin / golden-v3-fp32.bin |
各 779,584 B | 参照出力(make -C csrc int8-golden / test) |
saanotts-jp-v3-stage4.pt |
2,744,874 B | PyTorch のチェックポイント(上の blob はここから再現できます) |
k1-dict-438750.bin |
13,702,320 B | 漢字辞書(438,750 entries)。firmware に同梱済み |
SHA256SUMS.txt に全資産のハッシュがあります。
焼き方(スタックチャン)
# ⚠️ 先に元のファームを退避してください
esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX read_flash 0 0x1000000 backup.bin
esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX write_flash 0x0 m5-cores3-firmware-kanji-16mb.bin--flash_mode qio を渡さないでください。 ヘッダが QIO になると ROM ローダが読めず、
ets_loader.c 78 を繰り返してブートループします。そのまま焼けば bootloader が起動後に
qio_mode: Enabling default flash chip QIO で切り替えます。
焼けたら USB Serial/JTAG(115200)で繋ぎ、かな> に日本語の行をそのまま打ってください。
起動時に見える行
esp_psram: Found 8MB PSRAM device
saanotts: W8A8 + PIE 有効 / int8 blob を確認
saan_dict: esp_mmu_map OK: vaddr 0x3c960000
saan_dict: 辞書 OK: 見出し語 355768 / エントリ 438750 / 行列 1377x1377
saanotts: 起動直後: 内部 DRAM free 132039 B
今日は良い天気ですね。 を打つと 0xa69a7ebbb5ccb05f が出ます(この値は QEMU とも一致します)。
⚠️ できないこと・分かっていない食い違い
- 音の品質は測っていません。 聴いたのは 1 名・数文・対照なしです。
- 辞書を削ったので、ホストと音素の 0.32%が違います(n=298)。落ちた語は無音にならず、
短く切り直されて誤読されます(上毛→上+毛)。 - 端末に載らない後処理が 3 段あります(「何」の読み推定 / Sudachi の漢字読み修正 / 踊り字)。
中間表現で 2.00% [1.15, 3.45] の文が食い違います。 - アクセントの起伏が教師より 19% 大きい(
magnitude_ratio1.193)。過剰強調かは未判断です。 - 発話全体で見た RTF は 0.55〜0.71 で、まだ 0.5 を超えます(最初の 38 フレーム分の助走が
初回 pull に乗るため)。要件の分母が定義されていないので、両方を記録しています。 - I2S の実サンプルレートの誤差は未測定です(ESP32-S3 に APLL がありません)。
- 動作を確認した実機は M5Stack CoreS3 1 台だけです。
開発者向け
コマンドの名前が変わりました。 工程番号(K1〜K7)で付いていたファイル名を責務ベースにしました。
| 変更前 | 変更後 |
|---|---|
make -C csrc k2 / k4 / k4b / k5 / k6 / k7 |
jdict / accent / njd-rules / oj-heap / kanji-e2e / label-ids |
csrc/k1dict.c k4_accent.c k4b_njd.c k7_label2ids.c |
jdict.c accent.c njd_rules.c label_ids.c |
ESP32-S3 では W8A8 + PIE が既定になりました(-DSAAN_ENABLE_PIE=1 は不要)。
W8A32 で測るときは -DSAAN_ENABLE_PIE=0 を明示してください。
CI が増えました: 参照実装との一致(リリースの fp32 資産を落として実行・陽性対照つき)と、
ゲートが CI で回るかの機械検査を足しました。
訂正(この版で見つけた欠陥)
- C-055: 命令数が減っても実機で遅くなることがあります。GELU の最適化で objdump の命令数は
70 → 59 に減ったのに、実機では 118 → 211 cyc/要素と倍になっていました
(符号処理がスタック往復に落ちた)。QEMU でもマイクロベンチでも見えませんでした。 - C-056: ゴールデンテストが NaN を「完全一致」と報告して通していました。
出力の 27,136 サンプル中 26,368 が NaN なのにPearson 1.000000 / SNR inf dB / 一致と出ていました。
比較関数の 3 つの分岐が揃って NaN を「一致」側に落としていたためです。修正し、
毎回走る陽性対照を組み込みました。
全文は docs/decisions.md にあります。
v0.2.0 — 端末で漢字かな交じり文を扱う(⚠️ 実機未検証)
2026-09-01
ESP32-S3 が漢字かな交じり文をそのまま受け取って喋る firmware を追加した。
これまでは「ひらがな + アクセント記号」に変換したものをホスト側で作る必要があった。
かな> !今日は良い天気ですね。
形態素 7 個 / ids 53 個
⚠️ モデルは v0.1.1 と bit 同一
再学習していない。 saanotts-jp-v3-stage4.pt の SHA-256 は v0.1.1 と同じ
(4724bcf8a37a3c1518cc0219c1c515c6e8907410494459c91ff1c56f195890b5)。
漢字対応は入力経路(firmware + 辞書)だけの話で、モデルの入力は
以前と同じ 57 トークンの音素ID列。同じ文なら同じ ids になり、
同じ音が出る(QEMU で bit 一致を確認済み)。
したがって v0.1.1 の重みがそのまま使える。
releases/latest が v0.2.0 になった時点で、README が案内する
ダウンロードリンク 5 本が全部 404 相当になった(C-052)。
bit 同一のバイト列を v0.2.0 にも置くほうが、読者に
「latest ではなく 1 つ前のタグへ行け」と説明させるより安い。
v0.1.1 から持ってきた 8 本は SHA-256 を突き合わせて bit 同一を確認してある。
何が入っているか(15 本。latest だけで完結する)
| 資産 | サイズ | 中身 |
|---|---|---|
esp32s3-firmware-kanji-16mb.bin |
16,777,216 B | 漢字を読む 16 MB イメージ(app + model + dict)。焼くだけ |
esp32s3-firmware-w8a8-pie.bin |
2,806,624 B | かな入力・W8A8 + PIE(8 MB 以上)。焼くだけ。速度の本命 |
esp32s3-firmware-w8a32.bin |
2,806,624 B | かな入力・最適化なし。PIE の比較対照 |
k1-dict-438750.bin |
13,702,320 B | 辞書 blob 単体(ソースからビルドする人向け) |
saanotts-jp-v3-stage4.pt |
2,744,874 B | PyTorch の重み。 |
saanotts-jp-v3-samples.zip |
709,476 B | 合成音の WAV |
saanotts-jp-v3-int8.bin |
643,936 B | C99 コア用の int8 blob(マイコンに載るのはこれ) |
saanotts-jp-v3-fp32.bin |
2,249,792 B | 参照・デバッグ用の fp32 blob |
golden-v3-int8.bin / golden-v3-fp32.bin |
各 779,584 B | make -C csrc golden 用の参照出力 |
NOTICE.txt / NOTICE-openjtalk.txt / NOTICE-dictionary.txt |
第三者コードと辞書の帰属表示 | |
MODEL_CARD.md / LICENSE-MODEL.md |
既知の制約とモデルのライセンス | |
SHA256SUMS.txt |
全 15 本のハッシュ。 |
焼きかた
esptool.py --chip esp32s3 -p /dev/tty.usbmodemXXXX write_flash 0x0 \
esp32s3-firmware-kanji-16mb.bin! を前置した行が漢字かな交じり文として扱われる。! 無しは従来どおり
かな中間表現。
PIE の効果を測るなら v0.1.1 の esp32s3-firmware-w8a8-pie.bin と
esp32s3-firmware-w8a32.bin を比べること。
漢字版で知りたいのは「G2P に何 ms かかるか」の方。
かな入力のファームを焼く場合(8 MB ボード)
esptool.py --chip esp32s3 -p /dev/tty.usbmodemXXXX write_flash 0x0 \
esp32s3-firmware-w8a8-pie.binw8a8-pie と w8a32 を両方焼いて 定常 xRT を比べるのが、
このプロジェクトで一番知りたい測定である。
測ってあること
| MeCab との一致(辞書 + Viterbi + 未知語) | 1,977 / 1,977 文 |
| アクセント規則 4 段(Python 版との一致) | 2,333 / 2,333 文 |
| NJD チェーン(ホストとの一致) | 635 / 635 文 |
| ラベル → 音素ID(ホストとの一致) | 298 / 298 文 |
| 1 文あたりのピーク RAM | 104,589 B |
| app | 361,392 B / 2 MB(17.2%) |
| dict | 13,702,320 B / 13,828,096 B(99.1%) |
| 起動直後の内部 DRAM 空き | 54,232 B |
枝刈りの代償(フル辞書との差):
| 値 | |
|---|---|
| ホストと違う音素 | 0.32%(n=298) |
| SCOREQ の差(音の品質) | −0.0019 [CI が 0 を跨ぐ] |
枝刈りは音を汚さない。変えるのは読みだけ。
上毛(コーゲ)が 上(ジョー)+ 毛 のように切り直される。
地名と固有名詞で起きやすい。
⚠️ できていないこと
- 実機で一度も動かしていない。 QEMU で起動・辞書・錨の照合までしか
確認していない(I2S を有効にすると QEMU では合成が走らない) - 速度を誰も測っていない。 QEMU はサイクル精度ではない
- 漢字経路の音を聴いた人が 1 人もいない
- ホスト既定の後処理 3 段が載っていない(ONNX の「何」推定 / Sudachi /
踊り字)。中間表現で 2.00% の文が食い違い、エントリ数を上げても埋まらない
実機を持っている方への依頼は
esp32/TESTING.md。
ライセンス
- コードとドキュメント: MIT
- モデルの重み:
LICENSE-MODEL.md(v0.1.1 のものを使う) - Open JTalk 34 ファイル: 修正 BSD(Nagoya Institute of Technology / HTS Working Group)
- 辞書: 修正 BSD(NAIST + The UniDic Consortium)。TTS 用に枝刈り・形式変換した派生物
MODEL_CARD.md)。
v0.1.1 — 重みだけで音が出せるようになった / 焼くだけの ESP32 firmware
モデルは v0.1.0 と 1 バイトも変わっていません。
変わったのは「実際に動かせるかどうか」の方です。
| 重み・ブロブ・サンプル | v0.1.0 と bit 同一(SHA-256 で確認)。再学習していません |
| 変わったもの | 端末での自由入力 / 重みだけで合成できる経路 / 焼くだけの firmware / 動かなかった手順の訂正 |
🔴 v0.1.0 の手順は動きませんでした(訂正)
公開した「使い方」が 2 行とも、リリースだけを落とした人には通りませんでした。
| # | 何が起きたか |
|---|---|
| 1 | uv sync が失敗する — pyproject.toml の path 依存が piper-plus への絶対パスだった |
| 2 | --text(漢字)は教師 ckpt を要求する — 教師は private リポジトリにあり、外の人には原理的に実行できない |
手元には piper-plus も教師も揃っているので、書いたコマンドがそのまま通ってしまっていました。
「自分の環境で動いた」は「読者の環境で動く」ではない、という当たり前を踏みました。
MODEL_CARD.md の SHA-256 も、Release 本文だけ差し替え前の値を載せていました
(配布物と SHA256SUMS.txt は正しかったので shasum -c は通っていました)。
✅ 重みだけで音が出せるようになりました
piper-plus も教師 ckpt も要りません。 新規 clone で実測しました。
git clone https://github.com/ayutaz/sanoTTS-jp.git && cd sanoTTS-jp
uv venv && uv pip install "torch>=2.11" "numpy<2.5" "soundfile>=0.14"
uv run --no-project python scripts/synthesize_student.py \
--ckpt saanotts-jp-v3-stage4.pt \
--intermediate "きょ][おわよ][いて][んきです°ね" --out out/
# → out/cli_000.wav(22.05 kHz / 1.2 秒)「今日は良い天気ですね。」uv sync は使わないでください(piper-plus を要求します)。
生徒の推論に要るのは torch / numpy / soundfile の 3 つだけです。
できるようにした中身:
--intermediate— かな中間表現を直接受ける。教師 ckpt を 1 バイトも読まず、
教師経路(--text)と WAV がバイト単位で一致しますcsrc/g2p_table.json— かな→音素のテーブルを凍結。OpenJTalk が要らない。
読むたびに SHA-256 を検証し、piper-plus がある環境では live 表と全 195 件を突き合わせます
scripts/to_intermediate.py)には引き続き piper-plus が要ります。
かなを直接書くなら不要です。
🔊 ESP32-S3 でかなを自由入力できるようになりました
それまでのファームはビルド時に焼き込んだ 1 文しか喋れませんでした。
かな> こんにちわ
かな> きょ][おわよ][いて][んきです°ね
- 行編集は 369 B(
csrc/line.c)。UTF-8 対応の BS / CRLF / ESC の吸い込み / 溢れ検出
⚠️ 矢印キーのESC [ Aの[は中間表現では上昇アクセントです。
吸わないとカーソルを動かしただけでエラーも出さずに抑揚が変わります - コンソールは UART0 でも USB Serial/JTAG でも動きます
- 漢字・カタカナ・句読点は位置と文字を出して拒否します(黙って無音にしません)
QEMU の UART に実際に打ち込み、基準の 1 行から起動時と同じ checksum
(0x04de91103a0e49f9)が出ることを確認しました。
📦 新しい成果物: 焼くだけの firmware
ESP-IDF を入れなくても実機で試せます。esptool だけで焼けます。
| ファイル | 中身 |
|---|---|
esp32s3-firmware-w8a8-pie.bin |
W8A8 + PIE(ESP32-S3 の整数 SIMD)。★ こちらが本命 |
esp32s3-firmware-w8a32.bin |
最適化なし(移植可能 C)。比較用 |
pip install esptool
esptool.py --chip esp32s3 -p <ポート> write_flash 0x0 esp32s3-firmware-w8a8-pie.bin
# 115200 baud のシリアル端末で開く(screen / minicom / PuTTY など)bootloader + パーティション表 + アプリ + 重み blob が全部入っています
(offset 0x0 から 2.8 MB)。8 MB 以上の flash が要ります。
自分の配線に合わせてソースから作り直してください。 速度の測定だけなら DAC は不要です。
G2P が錨と一致 → プロンプト」までは確認しましたが、QEMU はサイクル精度ではないので
速度は測れません。
🙏 ESP32-S3 をお持ちの方へ — 残っている未検証項目は速度だけです。
15〜30 分で決着します。手順は
esp32/TESTING.md。
⚠️ v0.1.0 時点の手順書には欠陥が 2 件あり(1 行目のset-targetが落ちる /
USB ポート切り替えが黙って無視される)、新規 clone でなぞって直しました。
📊 品質(v0.1.0 から変更なし)
| 指標 | 生徒 | 教師 | 教師比 |
|---|---|---|---|
| SCOREQ synthetic/nr | 1.2716 | 1.9732 | 0.6444 [0.6044, 0.6843] |
| DNSMOS OVRL | 2.1730 | 2.7270 | 0.7969 [0.7756, 0.8173] |
| かな CER | 0.1671 | 0.1351 | 差 +0.0320(p=0.31、有意差なし) |
| アクセント符号一致 | 37/37 = 1.000 | — | — |
SCOREQ 2.4983 / UTMOS 2.3047 しか出ません。
📜 ライセンス(変更なし)
| 対象 | ライセンス |
|---|---|
| リポジトリのコードとドキュメント | MIT |
| 重み・ブロブ・サンプル・firmware イメージ | LICENSE-MODEL.md |
NOTICE.txt の
帰属表示を同梱し、出力の用途制限(個人攻撃 / 政治・宗教の主張 / アダルト /
素材としての再配布は不可)が下流にも伝播します。
一次データ: docs/measurements.md の M-63〜M-66
決定と訂正履歴: docs/decisions.md の D-040 / D-041 / C-039〜C-041
SHA-256
3ba8fdd7600fac8f450255435c7a837910506b319938d3821b5852251a786ea7 esp32s3-firmware-w8a32.bin
f18b0523345ead8787fe2fe89104de776aac8a2c9dd916285ab9a84ed6e67ef9 esp32s3-firmware-w8a8-pie.bin
b833f7025cf4dff8318847e9c5a620316bbbda3cb4f539baa112aa85eaf3ca81 golden-v3-fp32.bin
02641b6ea7ca2d44b99a5c205603a642a382162bc1ff3c74834ba4b07f5134bf golden-v3-int8.bin
82a16adad61837d5d2d8cfb1e0305bf5d2d61ce5431a5c0cb0f7938f69995028 LICENSE-MODEL.md
6d0f48a414680c7904fc430e47f79c2d8aec21f24fea6547b823d6926a9f77c6 MODEL_CARD.md
0b8681feeda97fc16ae99c57c3e7f2f818309c33aa03310242185bb7f1a6bda1 NOTICE.txt
dae6303c1105845aeedabc5141aa811f9d5de02f63d7af4188fb82e6cbe5298e saanotts-jp-v3-fp32.bin
c3b89216133fa7bee3f61ed9d8e6c7183a5dfd41b70dab194f42c20fce5b4170 saanotts-jp-v3-int8.bin
4991c8b532e29be182c7c190db393dd38bb0612053281adaf859569e7321f10c saanotts-jp-v3-samples.zip
4724bcf8a37a3c1518cc0219c1c515c6e8907410494459c91ff1c56f195890b5 saanotts-jp-v3-stage4.pt
v0.1.0 — 559 K パラメータ日本語 TTS の初回リリース
⚠️ これは古いリリースです。v0.1.1 を見てください。
モデルは v0.1.1 と bit 同一ですが、下の「使い方」は v0.1.1 で訂正されており、
v0.1.1 には焼くだけの ESP32 firmware が付いています。
559,008 パラメータの日本語 TTS。 arXiv:2608.21378
"sanoTTS" の蒸留レシピを日本語に適用した独立再実装の最初のリリースです。
教師は piper-plus (MB-iSTFT-VITS2)。
⚠️ 先に読んでください
| ライセンスはコードとモデルで違う | コードは MIT、モデルの重みは MIT ではありません → LICENSE-MODEL.md |
| 実機で動かしたことがありません | ESP32-S3 のボードが手元に無く、速度を一度も測っていない。C99 コアはホストと QEMU でしか走っていません |
| これは検証 (PoC) です | 製品品質ではありません。既知の制約は MODEL_CARD.md |
| 漢字は入力できません | 端末は「ひらがな + アクセント記号」を受け取ります。漢字→かなはホスト側でオフラインに行います |
📜 ライセンス — 重みは MIT ではありません
| 対象 | ライセンス |
|---|---|
| リポジトリのコードとドキュメント | MIT |
| このリリースで配布する重み・ブロブ・サンプル音声 | LICENSE-MODEL.md(LicenseRef-sanoTTS-jp-Model-1.0) |
なぜ分けたか。 MIT は「無制限に (without restriction)」の利用を認めます。
しかしこの重みはつくよみちゃんコーパスを素材に含む教師からの蒸留物で、
そのコーパスの条件は 帰属表示を必須とし、出力の用途に禁止事項を課し、
その義務が再配布を受けた側にも伝播すると定めています。
MIT を名乗ることは、こちらが持っていない権利を持っているかのように宣言することに
なるため、重みだけを別条件にしました。
できること
✅ 使用(商用を含む)/ ✅ 改変・派生物の作成 / ✅ 再配布・サブライセンス — 無償
必ず守ること(2 つ。下流にも伝播します)
1. 帰属表示を配布物に同梱する — NOTICE.txt をそのまま入れてください。
本ソフトウェアの音声合成には、フリー素材キャラクター「つくよみちゃん」
(© 夢前黎)が無料公開している音声データを使用しています。
https://tyc.rei-yumesaki.net/material/corpus/
(全文は NOTICE.txt。MOE-Speech / Common Voice / ROHAN4600 / ITA / JSUT /
piper-plus の表示も含まれます。1 行でも欠けると違反になります)
2. 生成音声を次の用途に使わない
❌ 個人・団体への攻撃、批判 / ❌ 政治・宗教上の主張 / ❌ アダルト用途 /
❌ 素材としての再配布(生成音声を「音声素材集」として配ること)
一次ソース: https://tyc.rei-yumesaki.net/material/corpus/
⚠️ 隠さずに書く既知のリスク
蒸留に使ったテキストのうち JSUT ver1.1 の 6,472 行は CC-BY-SA-4.0(継承付き)です。
「学習済みモデルは学習テキストの二次的著作物」という立場を取られた場合、
継承が重みに及ぶ可能性があります。日本の著作権法 30 条の 4 により学習自体が許され、
コーパス本文も再配布していないため実務上この立場が通る公算は低いと判断しましたが、
リスクはゼロではありません。
📦 成果物
| ファイル | サイズ | 中身 |
|---|---|---|
saanotts-jp-v3-stage4.pt |
2.7 MB | PyTorch checkpoint(fp32)。Python から推論・追加学習 |
saanotts-jp-v3-int8.bin |
643,936 B | C99 コア用 int8 ブロブ。マイコンに載せるのはこれ |
saanotts-jp-v3-fp32.bin |
2,249,792 B | 同 fp32 版(参照・デバッグ用) |
golden-v3-int8.bin / golden-v3-fp32.bin |
各 779,584 B | 移植を検証するゴールデン中間出力 |
saanotts-jp-v3-samples.zip |
709 KB | 合成音声サンプル 8 本(22.05 kHz / 21.5 秒) |
NOTICE.txt |
— | 必須の帰属表示(配布物に同梱すること) |
LICENSE-MODEL.md / MODEL_CARD.md |
— | ライセンス全文 / モデルカード |
SHA256SUMS.txt |
— | 上記すべての SHA-256 |
すべて runs/v3/stage4.pt(Stage 3 = 80,000 step)から生成しました。
csrc/export_i8.json / csrc/export.json に生成元 ckpt と SHA-256 が記録してあります。
📊 品質(held-out 24 文・すべて予測器スコア)
| 指標 | 生徒 | 教師 | 教師比 |
|---|---|---|---|
| SCOREQ synthetic/nr | 1.2716 | 1.9732 | 0.6444 [0.6044, 0.6843] |
| DNSMOS OVRL | 2.1730 | 2.7270 | 0.7969 [0.7756, 0.8173] |
| かな CER | 0.1671 | 0.1351 | 差 +0.0320 [−0.031, +0.087] p=0.31(有意差なし) |
| アクセント符号一致 | 37/37 = 1.000 | — | CI95 [1.000, 1.000] |
参考: 論文が報告する英語 embedded tier の SCOREQ 教師比は 0.5427。
int8 化のコスト(fp32 経路に対する SNR、n=24): 平均 28.11 dB / 最小 25.72 dB。
⚠️ この数字の読み方
- 絶対値を英語のモデルと比べないでください。 SCOREQ も UTMOS も DNSMOS も
日本語で較正されていません。日本語では実人間の音声ですら SCOREQ 2.4983 /
UTMOS 2.3047 しか出ません。論文自身も言語をまたいだ絶対値比較はできないと明言しています - 「教師比 0.64」は「教師の音を 100 としたとき 64」ではありません。
較正されていない予測器のスコアの比です ⚠️ 人による評価は 1 名の少数試行しかしていません。
v2 → v3 の指標上の改善は、盲検 A/B 7 試行で 1 度も聴き分けられませんでした。
「指標が向上した」と「人が良いと感じた」は別です
🧠 メモリと速度
| 実行時 RAM(ストリーミング版) | 197 KB(ESP32-S3 の SRAM 512 KB の 38%) |
| 重み(int8、flash) | 643,936 B |
| 速度 |
fp32 のまま移植すると 2.47× 実時間(間に合わない)という外挿値があります。
int8 + PIE(ESP32-S3 の SIMD)カーネルは実装済みで、QEMU 上でスカラ実装と
bit 完全一致することを確認しました(MAC の 99.40% を被覆)。
残っているのは実機でのサイクル実測だけです。
🔧 使い方
# ✗ 2 か所で失敗する
git clone https://github.com/ayutaz/sanoTTS-jp.git && cd sanoTTS-jp && uv sync
uv run python scripts/synthesize_student.py \
--ckpt saanotts-jp-v3-stage4.pt --text "今日は良い天気ですね。" --out out/uv syncが通らない —pyproject.tomlの[tool.uv.sources]が piper-plus への
絶対パスを指しているので、向け直すまでerror: Distribution not found at: file://...--text(漢字)は教師 ckpt を要求する — 音素 ID を教師のphoneme_id_map経由で
組むため。教師は private リポジトリにあるので、リリースだけでは使えない
正しい手順(piper-plus も教師も要らない。新規 clone で実測 = M-65):
git clone https://github.com/ayutaz/sanoTTS-jp.git && cd sanoTTS-jp
uv venv && uv pip install "torch>=2.11" "numpy<2.5" "soundfile>=0.14"
uv run --no-project python scripts/synthesize_student.py \
--ckpt saanotts-jp-v3-stage4.pt \
--intermediate "きょ][おわよ][いて][んきです°ね" --out out/uv sync は使わない。 生徒の推論に要るのは torch / numpy / soundfile だけで、
uv sync は piper-plus への path 依存を解決しようとして落ちる。
[ 上昇 / ] 下降核 / # 句境界 / ° 無声化。
漢字混じり文からの変換(scripts/to_intermediate.py)には OpenJTalk が要るので、
piper-plus のクローンが必要(教師 ckpt は不要)。
--intermediate と --text は同じ入力に対して WAV がバイト単位で一致する
(教師がある環境で確認済み。M-64)。
C99 コアの移植を検証する場合は golden-v3-*.bin を突き合わせてください
(make -C csrc all-test が実装例)。
🐛 このリリースで訂正した数値
生徒側だけ前後 0.3 秒のパディング無しの音声を、パディング済みの教師と
突き合わせていました。SCOREQ / DNSMOS はどちらも前後の無音に反応するため、
生徒だけが下駄を履いていました。
| 指標 | 誤(記録) | 正(前処理を揃えて測り直し) |
|---|---|---|
| SCOREQ 教師比 | 0.6613 | 0.6444 |
| DNSMOS OVRL 教師比 | 0.8385 | 0.7969 |
| かな CER(教師との差) | +0.0034 | +0.0320 |
アクセント(37/37)と int8 SNR(28.11 / 25.72 dB)は別経路なので影響を受けていません。
配布するモデルを選ぶ判断は変わりません — v3 は SCOREQ (+0.0653 [+0.022, +0.108]) と
DNSMOS (+0.0660 [+0.006, +0.124]) で v2 より有意に良く、アクセントと int8 SNR でも
上回り、CER で悪化していません。論文の英語比 0.5427 を上回る点も維持されます。
実際の差は +0.0320 で、v2 (+0.0425) からの改善は
−0.0105 [−0.037, +0.018] = 検出できません。
再現とゲート:
uv run --extra eval python scripts/release_metrics.py --out reports/release_v0.1.0(G1 が「比較する全セットが同じ長さの先頭無音を持つ」ことを検査し、
無パディングの陽性対照が落ちることまで確認します)
一次データ: docs/measurements.md(全項目に再現コマンド付き)
決定と訂正履歴: docs/decisions.md
SHA-256
MODEL_CARD.md の行だけ古い値 (6d9be727…) を載せていました
(差し替え前のファイルのハッシュ。2026-08-30 に訂正)。
配布物そのものと SHA256SUMS.txt は最初から下の値で正しいので、
shasum -a 256 -c SHA256SUMS.txt は通ります。
4724bcf8a37a3c1518cc0219c1c515c6e8907410494459c91ff1c56f195890b5 saanotts-jp-v3-stage4.pt
c3b89216133fa7bee3f61ed9d8e6c7183a5dfd41b70dab194f42c20fce5b4170 saanotts-jp-v3-int8.bin
dae6303c1105845aeedabc5141aa811f9d5de02f63d7af4188fb82e6cbe5298e saanotts-jp-v3-fp32.bin
02641b6ea7ca2d44b99a5c205603a642a382162bc1ff3c74834ba4b07f5134bf golden-v3-int8.bin
b833f7025cf4dff8318847e9c5a620316bbbda3cb4f539baa112aa85eaf3ca81 golden-v3-fp32.bin
4991c8b532e29be182c7c190db393dd38bb0612053281adaf859569e7321f10c saanotts-jp-v3-samples.zip
0b8681feeda97fc16ae99c57c3e7f2f818309c33aa03310242185bb7f1a6bda1 NOTICE.txt
51d0fec9f4e5a63a21ef5f493bcc900d73ed9efa927dc57d07893a8e7f207e07 LICENSE-MODEL.md
b80ea4a36c38b3d83e400d9570176041f70c1f681bd553e8cd71b7e3c47e66cc MODEL_CARD.md