mcpField
E
elevenlabs

elevenlabs

github.com/calesthioクリエイティブ・メディア73.0k1.1k 回インストール1 日前に更新
リポジトリ

elevenlabs について

OpenMontage 経由で ElevenLabs の音声、効果音、音楽生成と音声クローンを扱うスキルです。モデル別の制御方法や、Remotion のシーンにナレーションを同期する手順も説明します。

主な機能

  1. 01OpenMontage によるプロバイダールーティング
  2. 02テキスト読み上げモデルの選択
  3. 03音声クローン
  4. 04効果音と音楽の生成
  5. 05タイムスタンプ付き音声アラインメント
  6. 06Remotion シーンのナレーション手順

活用シーン

  1. 01動画シーンのナレーション生成
  2. 02字幕や演出を発話語句に同期
  3. 03効果音やBGMの作成
  4. 04クローン音声をナレーションに再利用

インストール

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

$npx skills add https://github.com/calesthio/openmontage --skill elevenlabs

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

同梱ファイルreference.md

詳細

ElevenLabs Audio Generation

現在の v4 トランスポート

v4、v4 Turbo、Instant Soundtracks、リアルタイムモデルについては、 10月の統合ガイドをお読みください。 v4 は Text to Dialogue を使用し、Turbo は dialogue WebSocket を使用します。 これらのモデルをレガシーのテキスト読み上げエンドポイントに送信しないでください。

OpenMontage のプロバイダールーティング

認証方法を選択する前に、OpenMontage のレジストリを確認してください。

  • fal_elevenlabs_tts が利用可能な場合は、これを優先してください。Eleven v3、 Multilingual v2、Turbo v2.5 を、中央管理された fal.ai 接続経由で利用できます。ElevenLabs の認証情報は別途必要ありません。
  • 直接プロバイダーの利用がレジストリで報告されている場合に限り、 elevenlabs_tts を使用してください。
  • 共有インストール環境では、ユーザーに .env の作成、キーのエクスポート、 認証情報の貼り付けを指示しないでください。直接プロバイダーにアクセスできない場合は、 管理者へのセットアップ依頼として案内してください。

以下の直接 API の例では、中央設定された ELEVENLABS_API_KEY が必要です。fal.ai プロバイダーが利用可能な場合、 これは既定の方法ではありません。

テキスト読み上げ

from elevenlabs.client import ElevenLabs
from elevenlabs import save, VoiceSettings
import os

client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))

audio = client.text_to_speech.convert(
    text="Welcome to my video!",
    voice_id="JBFqnCBsd6RMkjVDRZzb",
    model_id="eleven_multilingual_v2",
    voice_settings=VoiceSettings(
        stability=0.5,
        similarity_boost=0.75,
        style=0.5,
        speed=1.0
    )
)
save(audio, "voiceover.mp3")
モデル
モデル品質SSML サポート備考
eleven_multilingual_v2一貫性が最も高いなし安定しており、本番環境向け、29 言語に対応
eleven_flash_v2_5良好<break>、<phoneme>高速、一時停止/発音タグに対応
eleven_turbo_v2_5良好<break>、<phoneme>レイテンシーが最も低い
eleven_v3表現力が最も高いなしアルファ版 — 信頼性が低く、プロンプトエンジニアリングが必要

選択の目安: 信頼性を重視するなら multilingual_v2、SSML による制御には flash/turbo、最大限の表現力には v3 を選択してください(再生成が必要になる場合があります)。

スタイル別の音声設定
スタイルstabilitysimilaritystylespeed
ナチュラル/プロフェッショナル0.75-0.850.90.0-0.11.0
会話調0.5-0.60.850.3-0.40.9-1.0
エネルギッシュ/YouTuber0.3-0.50.750.5-0.71.0-1.1
セクション間の一時停止

flash/turbo モデルの場合: SSML の break タグをインラインで使用します。

...end of section. <break time="1.5s" /> Start of next...

break あたりの最大時間は 3 秒です。break が多すぎると、速度に不自然さが生じる場合があります。

multilingual_v2 / v3 の場合: SSML はサポートされていません。次の方法があります。

  • 段落区切り(空行)— 約 0.3~0.5 秒の自然な一時停止が入ります
  • ffmpeg で後処理する:音声を分割して無音を挿入します

警告: ...(省略記号)は信頼できる一時停止方法ではありません。単語や音として読み上げられることがあります。一時停止の手段として省略記号を使用しないでください。

発音の制御

発音に合わせた綴り(すべてのモデル): 発音させたいとおりに単語を書きます。

  • Janus → Jan-us
  • nginx → engine-x
  • ハイフン、大文字、アポストロフィを使って発音を誘導します

SSML の phoneme タグ(flash/turbo のみ):

<phoneme alphabet="ipa" ph="ˈdʒeɪnəs">Janus</phoneme>
反復ワークフロー
  1. 生成 → 聴く → 発音やペースの問題を特定する
  2. 調整する:発音に合わせた綴り、break タグ、音声設定
  3. 再生成する。一時停止の精度が十分でない場合は、TTS エンジンで調整し続けるのではなく、ffmpeg を使って後処理で無音を追加します。

音声のクローン作成

インスタント音声クローン
with open("sample.mp3", "rb") as f:
    voice = client.voices.ivc.create(
        name="My Voice",
        files=[f],
        remove_background_noise=True
    )
print(f"Voice ID: {voice.voice_id}")
  • client.voices.clone() ではなく client.voices.ivc.create() を使用します
  • ファイルハンドルはパスではなく、バイナリモード("rb")で渡します
  • まず m4a を変換します:ffmpeg -i input.m4a -codec:a libmp3lame -qscale:a 2 output.mp3
  • 複数のサンプル(2~3 個のクリップ)を使うと精度が向上します
  • 再利用できるよう、音声 ID を保存します

Professional Voice Clone: Creator プラン以上と 30 分以上の音声が必要です。reference.md を参照してください。

効果音

1 回の生成あたり最大 22 秒です。

result = client.text_to_sound_effects.convert(
    text="Thunder rumbling followed by heavy rain",
    duration_seconds=10,
    prompt_influence=0.3
)
with open("thunder.mp3", "wb") as f:
    for chunk in result:
        f.write(chunk)

プロンプトのヒント: 具体的に記述します — 「木の床板をゆっくりと慎重に踏みしめる重い足音。きしむ音を伴う」

音楽生成

10 秒から 5 分までです。.generate() ではなく client.music.compose() を使用します。

result = client.music.compose(
    prompt="Upbeat indie rock, catchy guitar riff, energetic drums, travel vlog",
    music_length_ms=60000,
    force_instrumental=True
)
with open("music.mp3", "wb") as f:
    for chunk in result:
        f.write(chunk)

プロンプトの構成: ジャンル、ムード、楽器、テンポ、用途を指定します。BGM の場合は「ボーカルなし」と追加するか、force_instrumental=True を使用します。

Remotion との統合

完全なワークフロー:台本から同期したシーンまで
VOICEOVER-SCRIPT.md → voiceover.py → public/audio/ → Remotion composition
        ↓                  ↓               ↓                 ↓
  Scene narration    Generate MP3    Audio files     <Audio> component
  with durations     per scene       with timing     synced to scenes
ステップ 1:シーンごとの音声を生成する

ツールキットのナレーションツールを使用して、各シーンの音声を生成します。

# Generate voiceover files for each scene
python tools/voiceover.py --scene-dir public/audio/scenes --json

# Output:
# public/audio/scenes/
#   ├── scene-01-title.mp3
#   ├── scene-02-problem.mp3
#   ├── scene-03-solution.mp3
#   └── manifest.json  (durations for each file)

manifest.json にはタイミング情報が含まれています。

{
  "scenes": [
    { "file": "scene-01-title.mp3", "duration": 4.2 },
    { "file": "scene-02-problem.mp3", "duration": 12.8 },
    { "file": "scene-03-solution.mp3", "duration": 15.3 }
  ],
  "totalDuration": 32.3
}
ステップ 2:Remotion のコンポジションで音声を使用する
// src/Composition.tsx
import { Audio, staticFile, Series, useVideoConfig } from 'remotion';

// Import scene components
import { TitleSlide } from './scenes/TitleSlide';
import { ProblemSlide } from './scenes/ProblemSlide';
import { SolutionSlide } from './scenes/SolutionSlide';

// Scene durations (from manifest.json, converted to frames at 30fps)
const SCENE_DURATIONS = {
  title: Math.ceil(4.2 * 30),      // 126 frames
  problem: Math.ceil(12.8 * 30),   // 384 frames
  solution: Math.ceil(15.3 * 30),  // 459 frames
};

export const MainComposition: React.FC = () => {
  return (
    <>
      {/* Scene sequence */}
      <Series>
        <Series.Sequence durationInFrames={SCENE_DURATIONS.title}>
          <TitleSlide />
        </Series.Sequence>
        <Series.Sequence durationInFrames={SCENE_DURATIONS.problem}>
          <ProblemSlide />
        </Series.Sequence>
        <Series.Sequence durationInFrames={SCENE_DURATIONS.solution}>
          <SolutionSlide />
        </Series.Sequence>
      </Series>

      {/* Audio track - plays continuously across all scenes */}
      <Audio src={staticFile('audio/voiceover.mp3')} volume={1} />

      {/* Optional: Background music at lower volume */}
      <Audio src={staticFile('audio/music.mp3')} volume={0.15} />
    </>
  );
};
ステップ 3:シーンごとの音声(別の方法)

より細かく制御するには、各シーンに個別に音声を追加します。

// src/scenes/ProblemSlide.tsx
import { Audio, staticFile, useCurrentFrame } from 'remotion';

export const ProblemSlide: React.FC = () => {
  const frame = useCurrentFrame();

  return (
    <div style={{ /* slide styles */ }}>
      <h1>The Problem</h1>
      {/* Scene content */}

      {/* Audio starts when this scene starts (frame 0 of this sequence) */}
      <Audio src={staticFile('audio/scenes/scene-02-problem.mp3')} />
    </div>
  );
};
ナレーションに合わせて映像を同期する

シーンの長さは、映像ではなく音声から算出します。

// src/config/timing.ts
import manifest from '../../public/audio/scenes/manifest.json';

const FPS = 30;

// Convert audio durations to frame counts
export const sceneDurations = manifest.scenes.reduce((acc, scene) => {
  const name = scene.file.replace(/^scene-\d+-/, '').replace('.mp3', '');
  acc[name] = Math.ceil(scene.duration * FPS);
  return acc;
}, {} as Record<string, number>);

// Usage in composition:
// <Series.Sequence durationInFrames={sceneDurations.title}>
単語レベルのタイミング(直接 elevenlabs_tts を使用)

字幕、単語に同期する見出し、またはアニメーションのキューを実際の 音声に合わせる必要がある場合は、音声を後から文字起こしするのではなく、 直接 elevenlabs_tts ツールに with_timestamps: true を渡します。

result = ElevenLabsTTS().execute({
    "text": paragraph,
    "voice_id": voice_id,
    "previous_text": prev_paragraph,   # continuity across separately synthesized paragraphs
    "next_text": next_paragraph,
    "with_timestamps": True,
    "output_path": "projects/<id>/assets/audio/tts/para_03.mp3",
})
result.data["word_timestamps"]   # [{"word": "Доверие.", "start": 0.12, "end": 0.71}, ...]
result.data["alignment_path"]    # para_03.mp3.alignment.json (raw character alignment)
  • 時刻は、その音声ファイルの開始からの秒数です。段落を 1 つのタイムラインに配置する場合は、各段落の配置オフセットを加算します。
  • 単語は空白で区切られ、句読点も保持されるため、スクリプトの text.split() の結果と一致します。
  • サービスによってテキストが正規化された場合(数字、記号など)、alignment_matches_text は false になります。この場合、タイミングはアラインメントにおける発話形式に従います。
  • 段落ごとに音声を合成すると、シーン切り替えに使える段落の境界を正確に取得できます。単語のタイミングを使えば、各段落内に字幕やキューを配置できます。
音声のタイミングパターン
import { Audio, Sequence, interpolate, useCurrentFrame } from 'remotion';

// Fade in audio
export const FadeInAudio: React.FC<{ src: string; fadeFrames?: number }> = ({
  src,
  fadeFrames = 30
}) => {
  const frame = useCurrentFrame();
  const volume = interpolate(frame, [0, fadeFrames], [0, 1], {
    extrapolateRight: 'clamp',
  });
  return <Audio src={src} volume={volume} />;
};

// Delayed audio start
export const DelayedAudio: React.FC<{ src: string; delayFrames: number }> = ({
  src,
  delayFrames
}) => (
  <Sequence from={delayFrames}>
    <Audio src={src} />
  </Sequence>
);

// Usage:
// <FadeInAudio src={staticFile('audio/music.mp3')} fadeFrames={60} />
// <DelayedAudio src={staticFile('audio/sfx/whoosh.mp3')} delayFrames={45} />
ナレーションとデモ動画の同期

シーンにナレーションとデモ動画の両方がある場合:

import { Audio, OffthreadVideo, staticFile, useVideoConfig } from 'remotion';

export const DemoScene: React.FC = () => {
  const { durationInFrames, fps } = useVideoConfig();

  // Calculate playback rate to fit demo into voiceover duration
  const demoDuration = 45; // seconds (original demo length)
  const sceneDuration = durationInFrames / fps; // seconds (from voiceover)
  const playbackRate = demoDuration / sceneDuration;

  return (
    <>
      <OffthreadVideo
        src={staticFile('demos/feature-demo.mp4')}
        playbackRate={playbackRate}
      />
      <Audio src={staticFile('audio/scenes/scene-04-demo.mp3')} />
    </>
  );
};
エラー処理
import { Audio, staticFile, delayRender, continueRender } from 'remotion';
import { useEffect, useState } from 'react';

export const SafeAudio: React.FC<{ src: string }> = ({ src }) => {
  const [handle] = useState(() => delayRender());
  const [audioReady, setAudioReady] = useState(false);

  useEffect(() => {
    const audio = new window.Audio(src);
    audio.oncanplaythrough = () => {
      setAudioReady(true);
      continueRender(handle);
    };
    audio.onerror = () => {
      console.error(`Failed to load audio: ${src}`);
      continueRender(handle); // Continue without audio rather than hang
    };
  }, [src, handle]);

  if (!audioReady) return null;
  return <Audio src={src} />;
};
ツールキットのコマンド:/generate-voiceover

/generate-voiceover コマンドで、ワークフロー全体を処理できます。

/generate-voiceover

1. Reads VOICEOVER-SCRIPT.md
2. Extracts narration for each scene
3. Generates audio via ElevenLabs API
4. Saves to public/audio/scenes/
5. Creates manifest.json with durations
6. Updates project.json with timing info

人気の音声

  • George:JBFqnCBsd6RMkjVDRZzb(温かみのあるナレーター)
  • Rachel:21m00Tcm4TlvDq8ikWAM(明瞭な女性の声)
  • Adam:pNInz6obpgDQGcFmaJgB(プロフェッショナルな男性の声)

一覧を表示:client.voices.get_all()

API の全ドキュメントについては、reference.md を参照してください。

コメント