Fish Audioの無料APIで自分の声を合成し、音声透かしをAudioSealとWavMarkで調べた記録(Irodori-TTSとの比較)
Fish Audioの無料APIで自分の声を合成し、音声透かしをAudioSealとWavMarkで調べた記録(Irodori-TTSとの比較)
Google タスクの今日の欄に「TTS を試す」が残っていた。 前日の 講座ナレーションを自分の声で作る TTS の調査 の続きで、今日は実際に音を出す番だった。
Claude Code にタスクを読ませると、Fish Audio の Drama 3 を試す項目が見つかった。 準備までは進めてもらったが、先へ進むには私が2点を決める必要があり、そこで止まった。
11月30日まで無料という告知
決める前に、Fish Audio の無料 API の告知記事 を Claude Code に渡し、どれだけ無料になるのかを聞いた。
答えは、ほぼ使い放題だった。
s2.1-pro-free というモデル名を指定すれば、2026年11月30日まで、文字数と回数の上限なしで無料で使える。
ただし、フェアユースポリシーの範囲内という条件が付く。
無料版の中身は有料版と同じ S2.1 Pro だ、とも Claude Code は説明した。
私が API キーを 1Password に登録し、Claude Code に叩かせた。 結果はモデルで分かれた。
| モデル | 結果 |
|---|---|
s2.1-pro-free(無料) | 通った |
drama-3-preview(Drama 3) | API クレジット不足の HTTP 402 で弾かれた |
この時点では自分の声を送っておらず、既製の声で試しただけである。 11月末まで無料なら面白い、と思った。 自分の声を送ることを私が了承すると、Claude Code は合成スクリプトを書き、無料モデルで1回目(23文)を流した。
2回目の合成が 1Password の前で止まる
2回目は、合成に入る前に止まった。
1Password からキーを読み出す op read が、2回続けて authorization timeout で失敗したのである。
10:12 の1回目は通っていた。 その後に 1Password アプリがロックされたのだろう、というのが Claude Code の見立てだった。 同じエラーが2回続いたので、プロジェクトのルールどおり issue に記録させた。
結局、承認ダイアログを出すために Claude Code が合成を流し直し、ダイアログが通ってから2回目も最後まで進んだ。 グリッド検査も通り、聞き比べページが更新された。
聞き比べて浮かんだ透かしへの疑問
聞き比べると、Irodori-TTS のほうが私の録音した声に近く、Fish のほうが AI 合成に近く聞こえた。
そこで引っかかったのは、画像生成では勝手に入る識別子のことだった。 AI の音声にも、合成だと分かる印が入るのだろうか。
Claude Code に調べさせると、足元から答えが出てきた。 手元の Irodori の環境に SilentCipher が入っていて、合成のたびに「IRDTS」という識別子を埋め込む作りだった。 合成したままの音声からは、IRDTS がはっきり読み出せた。 講座の本番音声は速度を変えてあり、その加工の後では読み出せなかった。 Fish の音声については、入っているとは確認できなかった。
調べた結果には Gemini の話も含まれていて、それで思い出した。 Gemini は Google のサービスなので、アカウントを BAN されたときなどを考えると面倒だから避けよう、と前に話していたのだった。 その理由も、Claude Code にメモへ書き足させた。
今回は Fish の無料版が普通に使えるので、そちらでやるか、とその場では考えた。 耳は Irodori に寄っているのに、条件は Fish に寄っている。
検出器を知らなければ透かしは確かめられない
Claude Code の説明によると、透かしの有無は原則として、検出する方法(検出器と、多くの場合は秘密の鍵)を知らないと確かめられない。 耳で聞こえず、統計的にも目立たないように埋め込むのが、透かしの設計目標だからである。 Irodori の透かしを読み出せたのも、Irodori がソニーの SilentCipher を使っていて、その検出器が公開されているからだった。
私は AudioSeal と WavMark も試すよう頼んだ。
Claude Code は AudioSeal(Meta)と WavMark の2つの検出器を当てた。 検出器が働くかを確かめるため、自分で透かしを入れた音声(陽性)と、私の録音(陰性)も一緒に判定した。
処理は10分を超えて、バックグラウンドに回った。 途中で torch の読み込みにつまずき、issue に記録させた。 GPU 版の torch で読み込めるようになってから、検出器を GPU で動かすようにスクリプトを直してもらった。
全件の判定が終わった結果は次のとおり。
| 音声 | AudioSeal | WavMark |
|---|---|---|
| Fish | 出なかった | 出なかった |
| Irodori(合成したまま) | 出なかった | 出なかった |
Irodori が使っているのは SilentCipher なので、Irodori から出ないのは想定どおりである。
なぜ2回目を作ったのか
ここで、なぜ2回目を作ったのかが分からなくなり、Claude Code に聞いた。
Fish で同じ文をもう一度作ると、どのくらい違う音になるか(生成のたびの結果のぶれ)を確かめるためだった。 Google タスクの手順6に「生成のたびの結果のぶれを記録する」と書いてあり、それに合わせて2回作ったという。 Fish の API にはシードを指定する項目がない。 そのため、同じ台本と同じ参照音声で頼んでも、毎回違う音が返ってくる。
そう聞いて、Irodori のほうが気になった。 Irodori は、狙った語だけシード値を変えて音を変え、ほかの部分は全く変えない、ということができるのか。 だとしたら、切り取って繋げているのか。
答えは、語単位ではなく文単位だった。 文ごとに作った音声をつなげている点は、私の推測どおりだった。 ただし切り取っているわけではなく、切れ目は文と文のあいだの無音にしか来ない。
Fish に透かしはあるのか、もう一度調べる
検出器2つで出なかったからといって、入っていないとは言えない。 私は Fish が透かしを入れていないかを、もう少し深く検索するよう頼み、Claude Code からサブエージェントに任せてもらった。
報告が届くと、要となる2点(運営会社と、ファイルにメタデータが入っていないこと)を Claude Code が自分でも確かめた。 結論は次のとおり。
- メタデータの目印は入っていないと言える
- 耳で聞こえない透かしは、公表も検出もなく「不明(入っていない寄り)」
- 確実に知るには、Fish Audio に直接問い合わせるしかない
透かしが見つからないという材料が、Fish の側に一つ増えた。 この日はこのあと、Irodori で読み替えが必要だった文を Fish に漢字のまま読ませる比較へ進んだ。
学び
- 音声透かしは、検出器(と多くの場合は鍵)を知らないと確かめられない。Irodori の IRDTS を読み出せたのは、SilentCipher の検出器が公開されていたからだった
- Fish の API にはシード指定がなく、同じ台本と参照音声でも毎回違う音が返る。Irodori の差し替えは語単位ではなく文単位で、切れ目は文と文のあいだの無音に来る