elevenlabs について
OpenMontage 経由で ElevenLabs の音声、効果音、音楽生成と音声クローンを扱うスキルです。モデル別の制御方法や、Remotion のシーンにナレーションを同期する手順も説明します。
主な機能
- 01OpenMontage によるプロバイダールーティング
- 02テキスト読み上げモデルの選択
- 03音声クローン
- 04効果音と音楽の生成
- 05タイムスタンプ付き音声アラインメント
- 06Remotion シーンのナレーション手順
活用シーン
- 01動画シーンのナレーション生成
- 02字幕や演出を発話語句に同期
- 03効果音やBGMの作成
- 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 を選択してください(再生成が必要になる場合があります)。
スタイル別の音声設定
| スタイル | stability | similarity | style | speed |
|---|---|---|---|---|
| ナチュラル/プロフェッショナル | 0.75-0.85 | 0.9 | 0.0-0.1 | 1.0 |
| 会話調 | 0.5-0.6 | 0.85 | 0.3-0.4 | 0.9-1.0 |
| エネルギッシュ/YouTuber | 0.3-0.5 | 0.75 | 0.5-0.7 | 1.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-usnginx→engine-x- ハイフン、大文字、アポストロフィを使って発音を誘導します
SSML の phoneme タグ(flash/turbo のみ):
<phoneme alphabet="ipa" ph="ˈdʒeɪnəs">Janus</phoneme>
反復ワークフロー
- 生成 → 聴く → 発音やペースの問題を特定する
- 調整する:発音に合わせた綴り、break タグ、音声設定
- 再生成する。一時停止の精度が十分でない場合は、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 を参照してください。