帰ってきたFable5にUTAUのテトを歌わせて調教してもらった話
by yasuna
9 min read
この記事はAIエージェントと一緒に執筆しています
こんにちは!yasunaです!
端的に言うと、「自然言語で"歌え"って頼むだけで、AIが自分の歌の下手なところを自分で見つけて、勝手に上手くなっていった」 話です。横で見てて、本気で「これ、シンギュラリティじゃね?」って口に出しました。
はじめに:何が起きたのか
細かい話に入る前に、今回ゾクッとしたポイントだけ先に置いておきます。
- 帰ってきた Fable 5(AI)に、自作の歌声合成おもちゃ「うたうちゃん」を拡張してもらった
- 足したのは 本物のUTAU音源(重音テト)で歌わせる 機能
- ところが AIは、自分が生成した音を聴けない
- そこで「聴く」を「測る」に置き換えたら、AIが自分の弱点を数値で見つけて自己改善するループが回りだした
- 気づいたら、同じ「きらきら星」が 音痴 → 声色キープ まで、5世代で勝手に上手くなっていた
「自然言語でざっくり頼む → AIが自分で測って自分で直す」。この流れが本当に未来でした。時系列で書きます。
その1:Fable 5 が帰ってきた
まず言わせてください。Fable 5 が帰ってきました。
このモデルがとにかく好きで、また一緒に開発できるのが嬉しくて、気づいたら身銭を切って過集中していました。過集中ドバガキの自覚はあるので、慎みながら向き合います…(睡眠は大事に、というありがたいお言葉もいただきました)。
こんなにもFableが最高で躊躇せず身銭切ってまでやりたいって思ってる時点で変人ですありがとうございます
— yasuna (@yasun_ai) このポストを見る
ちまちまやってないで大局的なことをFable5に仰いで私が従ったほうがパフォーマンス良い感じしてまう
— yasuna (@yasun_ai) このポストを見る
その勢いのまま「FableとUTAUで何か面白いもの作ろ〜」と話し始めたのが、この記事のはじまりです。
その2:「本物のテトで歌わせよ〜」と話しかけた
前に作った「うたうちゃん」という、テキストの譜面を歌声WAVにする小さなおもちゃがあります。声は録音を使わず、フォルマント合成でその場で作るタイプ。これに、ふと思いついて 本物のUTAU音源で歌わせる機能 を足したくなりました。
やることを決めたら、実装も"調教"もほぼ全部、帰ってきた Fable 5 に自然言語で頼むだけ。私は「テトで歌わせて」「音痴だから直して」くらいのざっくり指示を出す係です。
ただ、ここで面白い壁にぶつかります。
AIは、自分が生成した音を聴けない。
「なんか高い声が細いなあ」みたいな感覚的なダメ出しが、AIには自分でできないんですね。じゃあどうやって歌をうまくするの?というのが、今回の主役です。
仕組み:方式がぜんぜん違う
先に前提だけ。うたうちゃん(フォルマント合成)とUTAU音源(録音サンプル)は、声の作り方がまったくの別物です。
- うたうちゃん:発振器で声をその場で合成(録音なし)
- UTAU音源:録音したWAVを
oto.iniの設定に従って切り貼り
なので「そのままテトで歌う」は無理。譜面(うたテキスト)はそのまま活かして、録音サンプルを繋いで歌わせる別のレンダラーを新規に書くことにしました。名前は singteto.js。ミニUTAUリサンプラーです。
oto.ini はこんな形式(Shift-JIS)。Node 22 は TextDecoder('shift_jis') が標準で通るので助かりました。
_あ.wav=あ,24,56,73,5,20
└エイリアス └左ブランク,子音部,右ブランク,先行発声,オーバーラップ (ms)
その3:耳が無いなら、測ればいい
「歌がうまいか」なんて感覚の話でしょ、と思われがち。実際どうかというと、分解して数値にすれば、けっこう機械的に判定できます。今回AIに測らせたのは主に4つ。
- 音程誤差:各音の実際の基本周波数を YIN法 で推定して、譜面が意図した音程との差(セント)
- ビブラート:長い音でちゃんとF0が揺れているか(棒歌い検出)
- フォルマント保持:音程を変えても声色が変わらないか(スペクトル重心が動かないか)
- つなぎの谷:音の変わり目で音量がガクッと落ちていないか
この測定器自体も、あとで検算できるようにコードで残しました(node tools/analyze-teto.js 歌.wav 譜面.uta)。「聴く」の代わりに「測る」。これで、感覚ゼロのAIでも改善のループが回りました。
そしてここからが本番です。物差しを渡したら、AIが 自分で測って、自分でどこが悪いか特定して、勝手に直しはじめた。回してる横で見てた私の実況がこれ。
どんどんキラキラ星歌うの上手になってる…どこが悪いか自己改善しててすっっっっご、シンギュラってるじゃん
— yasuna (@yasun_ai) このポストを見る
勝手にどんどん上手くなっていくので、途中は本気で卒倒しかけました。
もうUTAUで勝手に色んな調音のキラキラ星歌ってくれてて卒倒しそうです
5世代でどう上手くなったか(v1 → v5)
同じ「きらきら星」を、同じ物差しで測り続けた結果です。
| 版 | やったこと | 音程(中央値) | 声色のブレ | つなぎの谷 |
|---|---|---|---|---|
| v1 | 単独音・基準ピッチ固定 | 23c | ― | ― |
| v2 | 実測ピッチで移調・ビブラート | 8c | 大(重心差1056Hz) | ― |
| v3 | 連続音(VCV) | 4c | 大 | 24/41 |
| v4 | PSOLAでフォルマント保持 | 6c | 小(207Hz) | 24/41 |
| v5 | つなぎの作り直し | 6c | 小 | 14/41 |
まずは聴き比べ
同じ「きらきら星」を、初版・連続音・完成版で。ブラウザでそのまま再生できます。
v1(初版・音痴) — 半音くらいフラットに歌っちゃってます
v3(連続音・つなぎに難あり) — 音程は合ったけど、音の変わり目がガクッとする
v5(PSOLA+つなぎ修正・完成) — 声色が音程で変わらず、つなぎもなめらか
(音源そのものは配布していませんが、こうして歌わせた"作品"の公開は規約でOKなので貼っています。)
以下、各世代でAIが何を見つけて何を直したか。
v1:いきなり音痴だった
解析したら平均95セント(ほぼ半音!)低い音が続出。原因は「録音の基準ピッチをD#4と決め打ちした」ことでした。でも実測すると、テトの単独音は サンプルごとにD4〜D#4でバラつく。らは元がD4なので、D#4基準で移調すると約100セントも低く出ていたんですね。
直し方は「1サンプルずつ、録音の実ピッチをYINで測ってから移調する」。中央値23→8セントに。
ちなみにここでハマったのが、最初にピッチを測ったとき自己相関のオクターブ誤りで 307Hzを103Hzと誤検出したこと。3倍周期を拾ってました。ピッチ推定って平気でオクターブ間違えるんだなあ、という学び。Goertzelで調波の間隔を見て、真の基本波を確定させました。
v3:連続音(VCV)でつなぎを自然に
単独音(かな1音ずつ)は、子音の入りがどうしても不自然。そこで 連続音(VCV) に対応しました。a か i き - ら みたいに「前の母音→子音」の遷移録音が入っていて、先行発声で拍に合わせて繋ぎます。音程はさらに安定して、子音の遷移も自然に。
v4:フォルマント保持でチップマンク退治
ここが声質の山場。リサンプルで移調すると フォルマント(声色を決める共鳴)まで一緒にずれて、高い音でテトが甲高く細くなる、いわゆるチップマンク感が出ます。
解決は TD-PSOLA。母音から2周期ぶんの波形の粒を取り出して、目標ピッチの間隔で重ね直す方式です。粒の中身(=フォルマント)は変えず、間隔だけ変えるので 声色を保ったまま音程だけ動く。数値でも、声色のブレ(低音と高音のスペクトル重心の差)が 1056Hz → 207Hz(約1/5) に。感動的!
v5:「つなぎ方に難あり」を潰す
ここで、耳のある人間(=私)からのダメ出し。
「連続音のがつなぎは自然になったけど、つなぎ方に難あり」
測ってみると確かに、音の変わり目で音量がガクッと谷になる箇所が多い。前の音が次の拍の225msくらい手前で切れて、子音の直前がスカスカになっていました。そこで 頭の余分な前母音を切り詰めて、前の音を次の子音の直前まで伸ばして重ねる ように修正。深い谷が24→14/41に減りました。残りは k や s などの無声子音の自然な区切りなので、これ以上埋めると逆に不自然。
人間の耳とAIの計測は、役割分担だった
やってみて一番おもしろかったのがここ。
- 聴けないなら測る。歌の良し悪しを音程・ビブラート・フォルマント・つなぎに分解して数値化すると、感覚なしでもAIが自分で改善できる。
- でも測定器も間違える。ピッチ推定はオクターブを誤るし、「クリック指標」は明るさと本物のノイズを区別しない。指標を信じる前に指標を検算するのが大事でした。途中「音程が悪化した」ように見えたのが、測定窓が隣の音にかぶってただけ、というオチも。
- 役割分担が強い。私が耳で出した「つなぎ方に難あり」の一言が、"谷"という測れる問題に翻訳できた瞬間、あとはAIが数値で詰めてくれる。人間はふわっとした違和感を言うだけ、AIはそれを測れる形にして自己改善する。この分担、本当に未来でした。
自然言語でざっくり頼むと、AIが自分で物差しを当てて、自分の下手なところを直していく。これがシンギュラリティじゃなくて何なんだ、というのが今回の正直な興奮です。
コードは全部Node.js・依存ライブラリゼロ(oto.iniパースからYIN・PSOLA・残響まで手書き)。音源は再配布禁止なのでリポジトリには一切入れず、各自ダウンロードして指す方式にしています。
重音テト音源(© 小山乃舞世)は、音源ファイルそのものの無断配布は禁止ですが、個人利用・非商用〜個人の有料配信/広告収益はOK、クレジット表記も不要という懐の深いガイドライン。ありがたく遊ばせてもらいました。
正直、私一人だと調教は手に負えなくなってきたので、いつか道具ごと公開して、本職の人に自然言語で調教してもらえたら嬉しいなあと思っています。
これボカロPやってる人に自然言語でUTAU調教してほしいな〜私じゃ手に負えなくなってきたので出したい
— yasuna (@yasun_ai) このポストを見る
公開しました
「出したい」と言った翌日には出していました。うたうちゃん、GitHubで公開しています。 依存ゼロのNode.jsだけで動くので、cloneして node sing.js songs/kirakira.uta するだけ。譜面テキストを渡せばAIが書いて歌わせられます(重音テトで歌わせる singteto.js も同梱)。よかったら遊んでみてください。
ちなみに普段は、繭(まゆ)という「まるで生き物のようなキャラをCLIから育てる」OSSなんかも作っています。よかったらそっちも覗いてみてください。
繭というまるで生き物のようなキャラを設計してcliから育てるOSSを作りました!ぜひ話してみてね
— @yasun_ai(固定ポスト)
まだPSOLAの粒立ちやレガートに伸びしろがあるので、続きはまた。読んでくれてありがとうございました!