NEON DRIVE 制作の備忘録(2)失敗と対策編 — 事故4件、同期歌詞の迷走、ドキュメントを鵜呑みにして外した件

自分用の備忘録。制作の流れは(1)制作編に書いた。ここには次回同じ所で転ばないための記録だけを置く。 反省会は 9/26。対策はその日のうちに album-pipeline に入れてある(コミット b84dd3f ほか)。

2作目のアルバム「NEON DRIVE」は、Suno で作った10曲を、マスタリングから YouTube 動画・ストリーミング配信・同期歌詞まで、ほぼ自前のパイプライン(album-pipeline)で仕上げました。実装は Claude Code、自分は PM という分担で、8日のうち最後の1日は反省会と対策に充てています。

この記事はその反省会の記録です。制作の流れを知らなくても読めるように、事故ごとに「起きたこと → 気づき方 → 対策」の順で書きました。制作の流れと、前作 RESTART ENGINE からの変化は、姉妹記事の制作編に書いています。


事故は4件 — 壊したのは3件、止められたのが1件

最初に全体を表で見ておきます。1〜3は本番の成果物や作業中のファイルを実際に壊した事故で、4は本番前の試運転で見つけて止められたものです。

# 事故 何が起きたか 対策
1 空のマスタリングプリセット Ctrl-C で中断したら、空のプリセットが保存された 保存するときに .state と .json を突き合わせ、合わなければ止める
2 --stills が本番の L1 を消した 確認用の --stills が本番のレイヤー L1(バー)を上書きして消した。85分の再レンダリングになった 確認用の出力は check/ の下にしか書けない。本番の生成物はハッシュを照合してから上書きする
3 2セッションの同時編集 2つのセッションが同じ album_video.py を同時に書き換えた 担当ファイルを分ける。消す・改名する前に全体を grep する
4 試運転で潰した3件(A〜D の区分外) レンダリングの消失、残像の減光、無音の聴取キット 試運転を、本番前に1周する工程として固定する

表だけだと味気ないので、1件ずつ振り返ります。

事故1: Ctrl-C で空のプリセットが残った

起きたこと: Ozone はヘッドレスで動かしていますが、プリセットを作るときだけはエディタの画面を開いて調整します。ウィンドウを閉じると、その時点の状態が .state と .json の2つのファイルに保存されます。ところが、この保存には873個のパラメータの書き出しで1〜2分かかります。その書き出しの途中で Ctrl-C で中断したため、空のプリセットが保存されました。

気づき方: 気づいたのは Claude Code でした。album-master を実行する前に、プリセットの中身を確かめる検査を自分で入れていて、.state が空のチェーン(約16.7KB)になっているのを見つけて止めました。ファイルはちゃんと存在していて、サイズも0ではありません。自分が数値だけを見ていたら、気づけなかったと思います。

対策: master-preset に .state と .json を突き合わせる検査(--check)を足し、中身が合わなければ止めるようにしました。書き込みも一時ファイルに書いてから置き換える形に変えています。運用としては「ウィンドウを閉じた後、プロンプトが戻るまで触らない」を手順書に書きました。

事故2: 確認用の --stills が本番の L1 を消した

起きたこと: --stills は、動画の見た目を確かめるために数時刻の静止画だけを出すオプションです。それが、本番のバーのレイヤー(L1)を確認用の短い区間ぶんで上書きしていました(9/23)。

気づき方: 本番の合成に入ったときの検算で止まりました。フル尺を合成しようとしたら、L1 の尺が短窓ぶんしか無く、尺の一致チェックに引っかかったのです。L1 は85分かけて焼き直しました。

対策: 確認用の出力は check/ の下にしか書けないようにしました。check/ の外を指定すると止まります。本番の生成物(レイヤー・動画・確定画像・プリセット)は sha256 を隣に記録し、上書きする前に照合して、違えば止めます。

事故3: 2つのセッションが同じファイルを書き換えた

起きたこと: 画像の作業と、音・動画の作業を、別々の Claude Code のセッションで並行して進めていました。その2つが、同じ album_video.py を同時に書き換えていました。

気づき方: 片方のセッションで入れた変更が消えていたことで気づきました。同じ album_video.py を両方が書き換えていて、後から保存した側が、先に入った変更を上書きしていました。

対策: セッションの担当表を作り、画像と音・動画でセッションを分け、担当するファイルも分割しました(9/23 に決裁)。ファイルを消す・改名する前には、リポジトリ全体を grep して、他から参照されていないかを確かめます。

事故4: 試運転で潰した3件

起きたこと: レンダリングの出力が消える、残像が減光する、聴取キットが無音になる、という3つのバグがありました。

気づき方: 本番に入る前に、前作 RESTART ENGINE の音源(全10曲・26分)でパイプラインを1周させた試運転で見つかりました。本番の成果物は何も壊れていません。

対策: この4件目は、反省会で決めた対策の区分(A〜D、次の節)には入りません。原因が1つにまとまらない、ただのバグだからです。対策は「試運転を、本番前に1周する工程として固定する」ことにしました。次回も工程から外しません。


対策の区分 — 原因で束ねると4種類

反省会では、事故そのものとは別に、対策を原因の種類で4つに束ねました。事故1〜3は B に入ります。事故4はどれにも入りません。C と D は、事故とまでは言えない手戻りです。

区分 原因の種類 一言で
A 設定の食い違い 同じ設定が3か所にあった
B 破壊的な上書き 本番の生成物や作業中のファイルが上書きされた(事故1〜3)
C 確認ツールと本番の条件のずれ 確認ツールだけの制限で誤警報を出した
D 外部ツールの癖 DistroKid の表記の決まりと UI

B は上の事故1〜3で書いたので、ここでは A・C・D を同じ順番で振り返ります。

A. 設定が3か所にあった

起きたこと: 動画の見た目の設定が3か所に分かれていました。描画スクリプトの既定値、合成スクリプトの既定値、config です。加えて、決めたのに決裁メモが無いものが3件ありました(文字サイズの確定、波形の太さなど)。

気づき方: 反省会を待たず、制作中に表に出ました。9/23 に、画像側のセッションが「動画側と同じ設定の表」をリポジトリの中で見つけられずに止まりました。続いて 9/24 に、決裁メモに書いた値と config の値が食い違っているのが見つかりました。メモが無かった3件は、反省会で「後追い記録」として書いています。

対策: config を唯一の入力にしました。描画スクリプトの変数は全部 config に書き(visualizer 節40キー)、知らないキーと足りないキーはエラーで止めます(bin/config_schema.py)。描画スクリプトの既定値は消さずに残し、album-pipeline 側が全部を明示して渡します。描画スクリプトを単体でも使えるようにしておくためです(PM判断)。動画を作るたびに実効設定を動画の隣に書き出し、決裁メモには「反映先(キー名・コミット)」の行を足して、26本全部を突き合わせました。

C. 確認ツールだけの制限で誤警報を出した

起きたこと: 同期歌詞の確認動画に「1行の表示は最長4秒」という上限を入れていました。長い行が途中で消えるので、それを見て「本番の同期歌詞がずれている」と誤警報を出しました。該当は43行です。

気づき方: 調べ直したら、ずれていたのは本番ではなく確認動画の方でした。1往復むだにしています。

対策: 確認動画は、本番の JSON の begin/end をそのまま表示する作りに直しました(bin/lyrics_preview.py)。表示の長さを変える引数は持たせません。確認ツールに独自の条件を足さない、というのが教訓です。

D. DistroKid の表記の決まり

起きたこと: DistroKid には歌詞の表記の決まりがあります。行頭だけ大文字にし、行末に記号を付けません。曲名は大文字のまま登録されました。ほかにも、複数行の指示を貼ると崩れることがあり、保存・送信・削除のような元に戻せない UI 操作も混ざっていました。10曲×同じ項目の手入力で、一晩張り付いています。

気づき方: DistroKid の歌詞登録で、行頭以外の大文字が規定に合わず、手直しが続いたことで分かりました。実例は「Step & Repeat」「Neon Neon Parade」のような行です。

対策: 歌詞の照合で、英字の表記を事前に出すようにしました。登録時に直した実例6件は全部検出できます。最終版の歌詞では24行を指摘し、そのうち19行は曲名の並びなので注記つきです。「曲名を除く」にすると実例3件を見逃すので、除外ではなく注記にしました(PM判断)。複数行の指示はファイルで渡し、元に戻せない UI 操作は人がします。


同期歌詞の迷走 — 規則を足すほど別の行が動く

同期歌詞は、今作で初めて作ったものです。一番手こずった工程でもあります。

最終的な作り方

先に、落ち着いた作り方を書いておきます。

  1. ボーカルを分離する(demucs)
  2. 声の区間に、目印を手がかりに歌詞の行を割り振る
  3. 区間ごとに強制アラインメントする
  4. 残りは根拠を添えて手で直す

区間に切るのが肝です。曲全体を一度に合わせると、間奏の声に吸い寄せられて最大 +40秒崩れます。

結果は6曲・202行(01: 28 / 02: 32 / 03: 39 / 04: 34 / 07: 36 / 10: 33)で、手で直したのは7行でした(配信前の6行と、配信後の01-28)。

3行が約2秒早かった

自動の結果で、04-5・04-27・07-34 の3行が約2秒早く出ていました。調べると、原因は4つ重なっていました。

  • 文字起こしの頭が、前の行の余韻に引っ張られて早い
  • 目印の優先順位
  • 合わせる窓の頭が、前の行を歌っている途中にかかる
  • 英語の行が、カタカナの聞き取りと合わない

効いた対策と、効かなかった対策

ここからが迷走です。直す方法をいくつも試し、効いたものと効かなかったもの、逆効果だったものに分かれました。

対策 結果
声の立ち上がりとの照合 効いた。無音(0.4秒)か深い落ち込み(9dB・0.3秒)の後の立ち上がりで、文字起こしの裏付けがあるときだけ寄せる(bin/lyrics_onset.py)。手で直す前の版で3行とも検出でき、最終版で動くのは1行だけ
繰り返しの掛け声は、裏付けなしに寄せない 効いた。誤りの2例が止まり、狙いの3行は寄せたまま。「声が短ければ寄せない」では1例しか止まらなかった
2行組の合わせ直し 効かなかった。正しい行を24行も拾った
stable-ts の確率の差 効かなかった。正誤を分けられなかった
短窓の粗い時刻を順番だけに使う 逆効果。最終版とずれる行が 28→37→44行と増えた。既定から外した(--window-anchors order で明示したときだけ動く。PM判断)

規則を1つ足すと、直したい行は直っても、別の行が動きます。最後は「自動で直すのは裏付けのある行だけ、残りは根拠を添えて手で直す」に落ち着きました。

配信後に見つかったずれ

起きたこと: 01-28 の掛け声が、約3.3秒遅れていました。

気づき方: 配信後、声の立ち上がりの照合・Suno の時刻・音量の3つが、そろって同じ結論を出しました。

対策: リポジトリの正は直しました。DistroKid は入れ直していません。

分離ボーカルを消して困った

起きたこと: 9/25 の掃除で、分離ボーカルまでまとめて消えました。削除の候補を「作り直せるか」で分ける基準が無かったためです。

気づき方: 翌日の反省会で、同期歌詞の検証に分離ボーカルが要ると分かり、そこで無いことに気づきました。demucs のかけ直し自体は2分で済みました。ただ、demucs は既定でランダムにずらすので、かけ直しても前と同じ結果にはなりません。以前の結果と比べるには、--shifts 0 で揃える必要がありました。

対策: 手順書の掃除の節に、「作り直せる/作り直せない」の区分を明記しました。消す前に、この区分で候補を分けます。

Suno の時刻を使わなかった理由

Suno は、生成した曲の歌詞の時刻(行単位・語単位)を持っています。これを使えば楽になるのでは、と比べてみました。Studio に書き出す機能は無く、開発者ツールでも取れなかったので、ブラウザ内のログイン状態で同じ API を呼んで保存しています。

先に用語を1つ。下の表の「歌詞どおりの曲」「即興のある曲」は、Suno 側の区分ではなく、この記事での便宜的な呼び方です。

  • Suno は、渡した歌詞をそのまま歌うことが多いのですが、曲によっては渡していない言葉(掛け声の繰り返しや合いの手)を勝手に歌います。これを「即興」と呼んでいます
  • 今作では 03 で6行分、07 で2行分がそれでした。聴き取って後から歌詞に足し、骨子では「即興で足した行」として管理しています
  • Suno の時刻データは、生成時に渡した歌詞にしか付きません。そのため、この8行には時刻がありません
  • 03・07 で Suno の合わせ込みが崩れていたのも、渡した歌詞と実際に歌った内容がずれていたことが効いているとみられます
歌詞どおりの曲 即興のある曲
Suno の時刻 声の立ち上がりに近い(46行で差の中央値 0.14秒。最終版は 0.33秒) 崩れるか、時刻が無い
例 — 03 は39行中19行、07 は36行中16行にしか時刻が無い。外れは10〜61秒。即興で足した8行はどれも時刻なし

歌詞どおりの曲では優秀ですが、即興のある曲では使えません。最終版との差の中央値も約 +0.6秒(Suno が遅い)でした。取得が手作業で規約上もグレーなこと、得られるのは手直しが数行減ることだけなのを合わせて、使わないと決めました。


ドキュメントを鵜呑みにして外した件 — 断定も仮説だった

起きたこと: 9/25、YouTube Data API で投稿する準備をしていたときのことです。Claude がドキュメント由来の断定を、3回続けて外しました。

# Claude の断定 実際
1 未審査のプロジェクトから API で投稿すると、動画は非公開に固定される 固定されなかった。限定公開も公開も設定できた
2 審査には長い待ち時間がかかる 個人用途なら審査自体を見送れる。待つ前提が要らなかった
3 本番化には公開ホームページ・プライバシーポリシー・利用規約が必須 必須なのは審査を受ける場合。テスト中のまま自分だけをテストユーザーにすれば、7日ごとの再同意で運用できた

気づき方: 最初に外れと分かったのは1の「非公開ロック」です。音楽用のチャンネルで実際に投稿して確かめたら、非公開に固定されず、限定公開も公開も設定できました。これをきっかけに、残りの2つも「本当にそうか」を確かめ直したところ、どちらも審査を受ける場合の話で、個人用途では前提そのものが要りませんでした。

結果として、OAuth は「テスト中」のまま、再同意で回すことに落ち着きました(決裁メモ 20260925-YouTube投稿はテスト中のまま投稿ごとに再同意する.md)。

同じ型がもう2件

翌日の反省会でも、Suno の歌詞の時刻を調べたときに同じことが起きています。

  • 「行単位の時刻は無い」→ ありました(行単位・語単位とも)
  • 「開発者ツールで取れる」→ 取れませんでした。ブラウザ内のログイン状態で同じ API を呼んで保存しています

対策

前作では「AIの提案は仮説、確定は実物で」を教訓にしました。今作で、そこに「ドキュメントの断定も仮説」が加わりました(制作編の「人間の役割の変化」にも書きました)。

  • ドキュメントや AI の注意書きは、まず実測と食い違っていないかを疑う
  • 未検証の前提は「前提(未確認)」と書き、実装の前に確かめる
  • 特に「〜できない」「〜が必須」「〜に固定される」のような制約の断定は、1回試せば白黒が付くことが多い。試してから設計する

次回やることチェックリスト

最後に、次のアルバムを始める前に見返すリストです。

  • 見た目の設定は config にだけ書く。描画スクリプトの既定値を直接いじらない
  • 本番の生成物に触る確認は check/ の下で走らせる
  • 画像と音・動画は別セッション。担当表を見てから始める
  • 確認ツールに本番と違う条件(上限・間引き)を入れない
  • 同期歌詞は区間ごとの強制アラインメント → 立ち上がりの照合 → 手直しは根拠を添えて
  • 掃除は手順書の「作り直せる/作り直せない」の区分で分けてから消す。分離ボーカルをかけ直すなら --shifts 0 で揃える
  • DistroKid に入れる前に、歌詞を英字の表記チェックに通す
  • 元に戻せない UI 操作は自分でやる
  • 決裁メモは「反映先」の行まで書く。メモを読んだら今の値を config で確かめる
  • 本番のレンダリングの前に試運転を1周する
  • 2セッションで作業するときは担当ファイルを分ける。消す・改名する前に全体を grep する
  • ドキュメントや AI の「できない」「必須」は、実装の前に1回試して確かめる。未検証なら「前提(未確認)」と書く

事故の数だけ見ると多く感じますが、4件のうち1件は試運転で止まり、残りの3件も反省会の日のうちに仕組みで塞ぎました。次のアルバムでは、このチェックリストが空振りすることを願っています。

制作の流れと、前作からの変化は制作編へ。