約7分で読めます

【ffmpeg】AI生成の曲と絵から、睡眠用アンビエント動画を組み立てる


目次

はじめに

「KASUMI DRIFT」という、寝るときに流すアンビエント音楽のYouTubeチャンネルを運営しています。1本が数十分の長尺で、霧のかかった夜の風景がゆっくり切り替わり、その上に途切れない音が流れ続ける、というだけの動画です。

素材はAIで作れます。曲は生成AI、絵も生成AI。ところが素材を並べただけでは、寝るための動画にはなりません。今回は、その「並べ方」をPythonとffmpegで作り込んだ話です。

抱えていた課題

  • 生成AIは「曲」を作ろうとする: Sunoにアンビエントと指示しても、ボーカルが入ったり、ドラムやビートが突然鳴り出したり、ドラマチックな展開を作ろうとして「寝られる音」から外れやすい。
  • AIが作る曲は短い: 生成される音源は1本が数分。長尺にするには複数本つなぐ必要があります。
  • つなぐと継ぎ目が痩せる: 生成音源は1本ごとに自前のフェードイン・フェードアウトを持っています。そのままクロスフェードすると、フェード済みの終わりとフェード済みの始まりが重なって、継ぎ目で音量がへこみます。
  • クリップの途中で曲が終わっていることがある: 1本の中で勝手に曲を終わらせて、数秒の無音のあとに再開する、という箇所が混ざります。
  • 静止画を長時間流すと単調になる: かといって激しく動かすと寝られません。

作った仕組みの概要

Sunoで睡眠用のアンビエント音源をプロンプト設計して生成し、それを自動で「1本の連続したトラック」に整えてから、映像を被せるというパイプラインを組み立てました。

1. Sunoで「眠りを妨げない」アンビエント音源を生成する

まず核となる音源の生成です。Sunoはメロディアスなポップスやボーカル曲を作るのが得意ですが、今回の目的は**「聴く人の意識に引っかからない、睡眠のための環境音・アンビエント」**です。

普通に生成ボタンを押すと、急にハイハットが刻まれ始めたり、サビでドラマチックに盛り上がったりして目が冴えてしまいます。そこで、以下のようなルールでプロンプトと設定を固定しました。

  • Instrumental(インスト)は必須: 人の声・歌詞が入ると脳が無意識に言葉を追ってしまうため、ボーカルは絶対に除外します。
  • ビート・ドラムの排除をタグで徹底する: スタイル指定に ambient drone, sleep meditation, soft warm analog synth pads, slow atmospheric soundscape を指定しつつ、no drums, no beats, no percussion, no sudden buildup といったドラムや急激な展開を抑え込む指示を組み合わせます。
  • 同じ世界観のクリップを素材プールに集める: Sunoの生成単位は1本あたり数分(2〜4分程度)。長尺の動画にするため、同じプロンプト設定から気に入ったテクスチャのクリップを複数本生成してストックします。

こうして「寝られる質感の音源素材」が揃うのですが、AIが生成したクリップをそのまま並べただけでは、自然な長尺トラックにはなりません。ここからがPythonとffmpegの出番です。

2. 生成クリップの自前フェードを検出して切り落とす

つなぐ前に、各クリップの頭と尻についているフェードを取り除きます。秒数を決め打ちにはできないので、毎秒のRMS(音量)を測って、中央値から一定dB下がっている区間をフェードとみなして切るようにしました。

そのうえで、

  • 統合ラウドネス(LUFS)を実測し、クリップごとに一定のゲインを当てて音量を揃える
  • 等パワークロスフェード(acrossfade の qsin カーブ)で連結する

という手順にしています。単純な線形クロスフェードだと重なった部分の音圧が落ちるので、等パワーであることが重要でした。

3. クリップの「途中で曲が終わる」箇所を見つけて割る

頭と尻を直しても、まだ問題が残ります。1本のクリップの内側で、

数秒かけてフェードアウト → 完全な無音が1〜4秒 → いきなり元の音量で復帰

という箇所が発生することがあるのです。就寝用途では、これが最悪の部類の不連続です。眠りかけているときに音が消えると、音が鳴っているときより強く意識に上がります。しかも復帰が急峻なので、そこで目が覚めます。

そこで、専用のスクリプトを別に用意しました。

  • 毎秒のRMSを見て、−45dBを下回る秒を「無音」とみなす
  • 見つけた無音から左右に辿り、中央値から8dB以上低い区間が続く間はフェードごと切り落とす
  • 残った前後を別々のピースとして書き出す(30秒未満のピースは捨てる)
  • 書き出したピースを、そのまま 2. のつなぎ処理に渡す

背景のテクスチャ音を重ねて誤魔化す案も試したのですが、復帰側の段差は隠れませんでした。隠すのではなく、切って詰めるのが正解でした。

4. 静止画にフェードをかけて長尺映像にする

映像側は、生成した数枚の静止画をゆっくり切り替えるだけの構成です。

  1. 画像1枚につき「フェードイン → 表示 → フェードアウト」のセグメント動画を作る
  2. セグメントをconcat(ストリームコピー)で連結する
  3. でき上がった映像の長さを実測し、その長さに音を合わせて多重化する

ポイントは、重いフィルタ処理をセグメント単位に閉じ込めて、連結は再エンコードなしで済ませることです。数十分の動画でも待ち時間が現実的な範囲に収まります。

5. タイトルとチャンネルアートもコードで作る

タイトルの焼き込みとチャンネルアートの生成は Pillow で書きました。デザインツールを開かなくても、曲名を変えて実行するだけで同じ体裁のものが出てきます。

やっていることは地味ですが、細かい調整が効きます。

  • 字間(トラッキング)を1文字ずつ手で送る。Pillowには字間の指定が無いので、1文字描くごとに次のX座標を自分で計算します
  • サブタイトルは、キャンバス幅の82%に収まるまでフォントサイズを自動で下げる
  • チャンネルバナーは、どの端末でも切れない1235×338のセーフエリアに文字を収める

タイトルは冒頭だけに表示したいので、映像の1セグメント目にだけオーバーレイし、9秒を過ぎたら消えるようにしています。

得られたメリット

「寝られる音」の基準が言語化できた

普通の音楽なら気にならない小さな段差が、就寝用途では致命傷になります。無音は音より目立つ、という基準を持てたことで、どこを直すべきかの判断が速くなりました。

素材が変わっても同じ品質で出せる

フェードの位置も、無音の位置も、毎回違います。それを秒数の決め打ちではなく測定で見つけるようにしたので、新しい素材をそのまま流し込めるようになりました。

1本あたりの作業時間が読めるようになった

曲をつなぐ、映像を作る、タイトルを焼く、という工程がすべてコマンドになったので、素材さえ揃えばあとは待つだけになりました。

使ったサービス・ツール

サービス/ツール役割使ってみての学び
Suno音源の生成インスト指定とドラム排除タグで「睡眠用」の質感を作る。1本が短く自前フェードも付くため、長尺化の後処理とセットで活用する
Gemini(画像生成)背景の静止画霧や夜景のような曖昧な絵は得意。生成物には透かしが入る点は把握しておく
ffmpeg音量解析・連結・映像合成ffprobe で毎秒のRMSを取れるので、音の解析まで一つで完結する。acrossfade の等パワーカーブが効く
Pillowタイトル・チャンネルアートの生成字間の指定が無いので1文字ずつ描く。逆に言えば1文字単位で制御できる
OpenCV画像処理(領域の検出と補修)「座標を決め打ちせず、画像から探す」実装に切り替えると仕様変更に強くなる
YouTube公開先長尺は再生時間が伸びやすい一方、冒頭の数十秒で離脱が決まる

まとめ

生成AIは素材を作るところまでは一瞬でやってくれますが、「作品として成立させる最後の1割」は自分で設計するしかないというのが、このチャンネルをやってみての実感です。しかもその1割は、聴く人の用途によって基準が変わります。

今回で言えば、それは「無音を作らない」でした。同じ素材でもBGM用なら気にしなくていい部分です。何のための動画かを決めると、直すべき場所が自然に決まる、という当たり前のことを手を動かして確認できました。