mcpField
P
papercut-voice

papercut-voice

github.com/0xs4n4tgクリエイティブ・メディア9952 回インストール2 週間前に更新
リポジトリ

papercut-voice について

papercut-voice は、Papercut の動画制作ワークフローでナレーションを作るスキルです。ローカルの Qwen3-TTS、自分の録音や別の TTS、仮音声に対応し、単語単位のタイミング、字幕、シーンプランを生成します。

主な機能

  1. 013 種類のナレーション制作方法
  2. 02行ごとの WAV 生成と選定
  3. 03ASR による単語タイミング生成
  4. 04字幕とシーンプランの作成
  5. 05仮音声の後からの差し替え

活用シーン

  1. 01台本からナレーション音声を作成
  2. 02発話に合わせて字幕タイミングを調整
  3. 03仮音声で動画を組み立ててから再録音

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/0xs4n4tg/papercut-explainer --skill papercut-voice

インストール後、Claude Code で/papercut-voiceを入力して読み込まれたか確認します。

詳細

Papercut の音声

後続の各ステージでは、ナレーションフォルダーに次の2つが必要です。voice/final_narrator/<id>.wav(スクリプトの各行につき1つの WAV)とtiming_narrator.js(各単語の開始時刻と終了時刻)。このスキルでは、3つの方法のいずれかでこれらを作成し、その後、字幕とシーンプランを作成します。スクリプトの完全なリファレンスはlibrary/voice/README.mdです。

事前確認

  • ライブラリ:$PAPERCUT_LIB、それがなければ~/.config/papercut-explainer/config.env内のPAPERCUT_LIB=の行、それもなければ一度だけ確認します。環境変数に設定し、. "$PAPERCUT_LIB/env.sh"を実行すると、ASR_PY、QWEN_TTS_PYやその他の設定値がシェルに読み込まれます。
  • 足場を作成済みのプロジェクト(papercut-build、ステップ1)。narration/script.jsonと、scenesを含むpapercut/episode.jsonが必要です。new_episode.shはbuild_timing.pyをnarration/にコピーします。
  • ユーザーがすでに TTS をインストールしている場合は、そのまま使用し、再構築しないでください(パス B、または Qwen3-TTS の場合は A)。何を使っていて、どこで実行するのかを一度だけ確認してください。そうでない場合は、どのパスにするか尋ね、必要なものを伝えてください。
パス必要なものインストール方法
A. ローカル Qwen3-TTSlibrary/voice/setup_tts.shの音声用 venv(モデルのダウンロードは 2.3~4.2 GB)フル
B. 自分の録音または別の TTSASR バックエンド(mlx-whisper または faster-whisper)、およびピッチ処理用の librosa + numpy。ASR_PYとQWEN_TTS_PYが指定する Python 環境に必要ですフル、またはこれらのパッケージを手動でインストール
C. 仮音声ffmpeg。--sayには macOS が必要です標準

選択したパスに、インストール済みの環境以上のものが必要な場合は、./install.sh --doctorを実行してpapercut-setupに引き継いでください。ASR がない場合、build_timing.pyは1秒あたり2.75語として推定するだけなので、字幕のタイミングが実際の音声とずれていきます。

パス A:ローカル Qwen3-TTS

シリーズフォルダーから実行します。

cp $PAPERCUT_LIB/voice/voice.example.json voice/voice.json     # once per series; edit it
$PAPERCUT_LIB/voice/narrate.sh voice/voice.json episodes/NN-slug/narration
$PAPERCUT_LIB/voice/finalize.sh voice/voice.json episodes/NN-slug/narration episodes/NN-slug/narration/voice/takes/narrator [num.json]
  • voice.json:speaker(CustomVoice:Aiden、Dylan、Eric、Ryan、Serena、Vivian、Sohee、Ono_anna、Uncle_fu)とinstructを指定します。推奨設定:CustomVoice 1.7B、Ryan または Aiden、指示文は 「friendly, natural, conversational, like telling a friend; clear pauses between sentences; stress the key word of each line」。takes_per_lineのデフォルトは3です。tempoは1.0のままにします(ほかの値にはrubberbandが必要です)。qa_targetsでは、ピッチ範囲と ASR の一致度(0.9)を設定します。
  • スクリプトの行には、tts(発音用に書き換えたテキスト。字幕ではtextを維持)と、その行専用のinstructを指定できます。num.jsonでは数字を読み上げ用の語句に対応付けます({"13,000": "thirteen thousand"})。これにより、数字が原因で ASR の照合に失敗するのを防げます。
  • TTS プロセスは一度に1つだけ実行してください。1.7B モデルは多くのメモリを消費します。
  • finalize.shは各テイクで ASR とピッチ処理を実行し、行ごとに1つ選択して、無音部分をトリミングし、音量を-16 LUFSに正規化します。その後、最終音声でもう一度 ASR を実行し、timing_narrator.jsを書き出します。音声を聞いた後、修正をvoice/picks_override.json({"l3_name": 2})に記入し、SKIP_PICK=1を指定して再実行してください。
  • Voice Design(言葉で音声を説明する機能)は Qwen3-TTS のモードですが、narrate.pyでは制御できません。ほかの場所で生成し、パス B を使用してください。

パス B:自分の録音または別の TTS

  • 1行につき1つの WAV をvoice/final_narrator/<id>.wavに配置するか、未処理のテイクをvoice/takes/narrator/<id>_<k>.wavに配置し、上記のとおりfinalize.shを実行します。
  • 完成した音声については、書き起こしを作成し、voice/chosen_narrator.jsonを{id: "voice/final_narrator/<id>.wav"}として記述してから、narration/で次を実行します。
$ASR_PY $PAPERCUT_LIB/voice/asr.py voice/final_narrator/*.wav > voice/asr_narrator_final.json
CHOSEN=voice/chosen_narrator.json TIMING_OUT=timing_narrator.js python3 build_timing.py voice/asr_narrator_final.json

パス C:仮音声

narration/フォルダーから実行します。

python3 $PAPERCUT_LIB/tools/placeholder_vo.py [--wps 3.0] [--say [VOICE]]

script.jsonだけを使って、無音の WAV(または--sayを指定した場合は macOS のsay音声)とtiming_narrator.jsを書き出します。単語の長さに応じて単語ごとの時間を割り当てます。--wps 3.5から4を指定すると、目標のペースに近づきます(デフォルトは3.0)。リールを先に作成してタイミングを調整し、後から音声を差し替えることもできます。WAV を差し替え、finalize.shを再実行し、build_vo.pyを再実行すると、単語のキューも追従します。

語り方のルール

語りは人間らしく、ゆったりとしていて、強調が感じられるものにします。視聴者が初めて聞いたときに、すべての行を追える必要があります。

  • 間を取る。 文末ごと、およびリストの各項目の間(「そうです。そうです。そうです。」)に、しっかり間を置きます。まずは行間を0.3〜0.4秒に設定し(episode.json の gap。デフォルトは0.22。gaps は指定した行の後に追加の間を入れます)、耳で確かめて調整します。間は文や節の切れ目にのみ入れます。「それをコピーした、| そうすることで……」のように、フレーズの途中に0.2秒の間を入れると、遅れているように聞こえます。間を削るのではなく、尺は上限の30秒を使い、テンポを落として調整することは避けてください。
  • 強調。 各行で強く聞かせる1〜2語(数字、意外性のある言葉、結論)は、スクリプトの演出表に従います。行ごとの instruct として渡します。
  • トーン。 会話調で、少し楽しげにしますが、アナウンサーのような勢いは出しません。質問は本当の質問らしく、決めの一言は好奇心を誘い、CTAは温かみのあるものにします。ナレーター自身は冗談を言わず、映像が冗談を担います。
  • クローンモデル(指示なし)。 クローン音声(ref_audio + ref_text、Baseモデル)では、調整できるのはテキストとテイクです。句読点で間を表現します(リストの各項目の間には句点を置き、短い質問は独立した文にします)。連続したテイクを複数のシード(SEED_BASE、takes_per_line)で生成し、狙った語のピッチ変化が最も大きく、ASRが0.9以上のものを選びます。平板な行は単独で録り直し(ONLY=l4_peak)、文の切れ目でつなぎます。使用する権利のある音声のみをクローンしてください。
  • スクリプト全体を一度に生成すると、韻律に一貫性が出ます。テイクを1つ生成し、split_take.py で行ごとに分割してから、episode.json で "gap": 0 を設定します。ユーザーが希望する場合は、シーンごとの生成でもかまいません。
  • 全体を実行する前に、最初の3行をユーザーに聞かせてください:ONLY=l1_hook,l2_...,l3_... narrate.sh ...。

字幕とタイミング

papercut/ から、episode.json に scenes が設定された後に実行します:

python3 $PAPERCUT_LIB/tools/build_vo.py     # public/vo/voiceover.wav, public/vo/captions.json, src/plan.json
python3 $PAPERCUT_LIB/tools/timing_md.py    # Timing section of STORYBOARD.md

captions.json はRemotionの Caption[] です。シーンの境界は単語のタイムスタンプから決まり、コンポジションの長さは plan.json の durationInFrames です。build_vo.py は1.2秒未満のシーンがあると警告します。ライブラリがない場合は、Remotion captions skill(whisper.cpp)または単語のタイムスタンプを出力できる任意の音声認識ツールでWAVを書き起こします。

字幕は単語ごとに破れたタグを1つ配置し、1ページあたり1〜3語、毎秒3.5〜4語にします(../papercut-explainer/references/style-guide.md のセクション11および13)。

引き継ぎ

次は papercut-build(スタブ、シーン、音声、レンダリング)です。src/plan.json と papercut/STORYBOARD.md のTimingセクションを読み込みます。後から音声を変更した場合は、build_vo.py と timing_md.py を再実行してください。その後、シーンキャッシュによってすべてが再レンダリングされます。

コメント