mcpField
E
elevenlabs

elevenlabs

github.com/digitalsamba创意与媒体2.4k1.2k 次安装今天更新
代码仓库

关于 elevenlabs

通过 ElevenLabs API 生成和处理音频的工具包技能,涵盖语音合成、转录、声音克隆、音效和音乐。还介绍将分场旁白与 Remotion 场景同步的工作流。

主要功能

  1. 01使用多种模型生成语音
  2. 02使用 Scribe 转录音频
  3. 03创建即时声音克隆
  4. 04生成音效和音乐
  5. 05为 Remotion 对齐场景音频

适用场景

  1. 01制作带旁白的视频场景
  2. 02转录音频并对齐词语时间
  3. 03根据音频样本克隆声音
  4. 04生成音效或背景音乐

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/digitalsamba/claude-code-video-toolkit --skill elevenlabs

安装后在 Claude Code 中输入/elevenlabs确认已加载。

附带文件reference.md

详细介绍

ElevenLabs 音频生成

需要在 .env 中设置 ELEVENLABS_API_KEY。

文本转语音

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 种语言。Toolkit 默认模型;支持风格和语速
eleven_v4表现力最强无(音频标签、IPA)最新模型(发布于 2026-09-28),支持 90 多种语言,克隆准确度最高。仅支持稳定性和相似度,不支持风格或语速
eleven_flash_v2_5良好<break>、<phoneme>速度最快、价格最低;支持暂停和发音标签
eleven_v3表现力强无上一代模型——优先使用 v4

**选择:**追求可靠性以及风格和语速控制时,选择 multilingual_v2;追求最强表现力和克隆语音时,选择 v4(测试几次生成结果);需要 SSML 控制时,选择 flash。

已弃用:eleven_turbo_v2_5 → 使用 eleven_flash_v2_5(质量相同,延迟更低;参见已弃用模型)。Toolkit 仍接受 --model eleven_turbo_v2_5:它会输出一行警告,并改为发送 eleven_flash_v2_5。

Eleven v4

发布于 2026-09-28(更新日志,模型页面)。Toolkit 通过 Text to Dialogue API 调用 v4,并只传入一个输入(旁白语音),这是 ElevenLabs 对内容创作和长篇音频的推荐方式:

from elevenlabs import DialogueInput

audio = client.text_to_dialogue.convert(
    inputs=[DialogueInput(text="[warm] Welcome to my video!", voice_id="JBFqnCBsd6RMkjVDRZzb")],
    model_id="eleven_v4",
    settings={"stability": 0.5, "similarity": 0.75},  # the only two settings v4 takes
)
save(audio, "voiceover.mp3")
  • uv run tools/voiceover.py --model eleven_v4 ...(以及 redub.py --model eleven_v4)会自动执行上述操作,提示一次 --style/--speed 会被忽略,并在句子边界处分割每个请求中超过约 2,000 个字符的文本(这是可靠的对话长度限制)。请改用 --max-wpm 控制语速。
  • client.text_to_dialogue.convert_with_timestamps(...) 返回的字符对齐信息与 TTS convert_with_timestamps 相同,因此 redub.py --sync 可用于 v4,并会发送相同的稳定性和相似度设置。
  • 将 settings 作为字典传入:2.69 之前版本的 SDK 会使用仅包含稳定性的模型对其进行类型标注。自 2026-09-28 起,API 会在此处读取 similarity(值超出范围时会返回 422 错误)。Text to Dialogue 需要 elevenlabs>=2.21。
  • 使用音频标签([whispering]、[warm]、[sigh])和标点符号直接生成。请使用清楚描述语音的标签,因为 v4 也可能将标签解读为音效。
  • eleven_v4_turbo 是实时版本,仅可通过 Text to Dialogue WebSocket 使用。Toolkit 不使用此版本,因为离线渲染无法从低延迟中受益。
按风格设置语音参数
风格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 模型时:**在文本中内联使用 SSML break 标签:

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

每个 break 最长 3 秒。过多的 break 可能导致语速异常。

**使用 multilingual_v2 / v3 / v4 时:**不支持 SSML。可选方式:

  • 分段(空行)——会产生约 0.3-0.5 秒的自然停顿
  • v3/v4:使用 [long pause] 等音频标签
  • 使用 ffmpeg 后期处理:分割音频并插入静音

警告:...(省略号)不是可靠的停顿方式——它可能会被读成一个词或声音。请勿使用省略号来制造停顿。

发音控制

**语音拼写(任意模型):**按期望的发音拼写单词:

  • Janus → Jan-us
  • nginx → engine-x
  • 使用连字符、大写字母和撇号来引导发音

SSML 音素标签(仅限 flash):

<phoneme alphabet="ipa" ph="ˈdʒeɪnəs">Janus</phoneme>

**v4 中的 IPA:**无需标签。用斜线括起 IPA,并用双引号替代原词:

The term "/ˌbaɪoʊˈkemɪstri/" refers to the study of chemical processes.
迭代工作流
  1. 生成 → 聆听 → 找出发音和节奏问题
  2. 调整:音标拼写、停顿标签、语音设置
  3. 重新生成。如果停顿不够精准,请使用 ffmpeg 在后期添加静音,而不是反复调整 TTS 引擎。

语音转文本(Scribe)

with open("voiceover.mp3", "rb") as f:
    result = client.speech_to_text.convert(file=f, model_id="scribe_v2", tag_audio_events=False)
words = [w for w in result.words if w.type == "word"]  # .text, .start, .end (seconds)

scribe_v2 是当前版本,也是工具包的默认版本。scribe_v1 已弃用,并已宣布将于 2026-07-09 移除(更新日志),但目前仍可用,因此 --stt-model scribe_v1(redub)和 --model scribe_v1(align_captions)仍会运行该版本,并显示警告。scribe_v1_experimental 是 scribe_v1 的 2025 年预览版,接受时会显示警告,并以 scribe_v2 发送。工具包在 redub.py(转录 + --sync 词语时间)和 align_captions.py 中使用 Scribe。

声音克隆

即时声音克隆
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.ivc.create()(而非 client.voices.clone())
  • 以二进制模式("rb")传入文件句柄,而非路径
  • 先转换 m4a:ffmpeg -i input.m4a -codec:a libmp3lame -qscale:a 2 output.mp3
  • 使用多个样本(2-3 段音频)可提高准确度
  • 保存声音 ID 以便重复使用

**专业声音克隆:**需要 Creator 计划或更高版本,以及 30 分钟以上的音频。请参阅 reference.md。

音效

每次生成最长 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 分钟。使用 client.music.compose()(而非 .generate())。

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)

**提示词结构:**流派、情绪、乐器、节奏、使用场景。生成背景音乐时,添加“no vocals”,或使用 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
uv run 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}>
音频时间安排模式
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。

评论

支持 Markdown