首页
/ Remotion 音效库开发实战:向 @remotion/sfx 添加新音效的完整工作流

Remotion 音效库开发实战:向 @remotion/sfx 添加新音效的完整工作流

2026-09-05 12:37:30作者:尤峻淳Whitney

Remotion(Make videos programmatically with React)内置了一个 CC0 授权的音效 URL 库 @remotion/sfx,它本身只是一个"字符串常量包",真正复杂的是其背后横跨素材仓库、文档站、目录注册与波形生成的多包协作流程。本文以官方贡献技能文档 .agents/skills/add-sfx/SKILL.md 为主线,完整拆解从素材入库、导出声明、文档页创建到构建发布的 7 个步骤,并结合 sfx 包源码remotion-media 生成脚本波形采样脚本 说明每一步在仓库中的实际落点,读完即可独立完成一个新音效从 0 到上线的全链路操作。

前置条件:素材必须先进入 remotion.media

在动 @remotion/sfx 之前,必须先在 remotion-dev/remotion.media 素材仓库中登记音效文件,本仓库中的 packages/remotion-media 就是该流程在本 monorepo 中的对应物。仓库对素材有硬性要求:

  • WAV 格式(未压缩 PCM,保证跨浏览器与渲染器解码一致);
  • CC0(Creative Commons 0)授权,且必须保留出处信息;
  • 峰值归一化到 -3dB,避免不同音效之间响度失衡。

此外,文档指出 .env 文件在 worktree 环境中可能缺失,只在主分支(非 worktree)上保证存在,构建部署前需确认环境完整。

source of truth:generate.ts

remotion.media 仓库中的 generate.ts 是唯一事实来源(source of truth)——一个音效必须先在 soundEffects 数组中登记,才能被复制到发布目录并写入 variants.json。对照本仓库的 packages/remotion-media/generate.ts 可以看到这段逻辑的真实实现:

// Sound effects (pre-existing files, CC0 licensed)
const soundEffects = [
	{
		fileName: 'whoosh.wav',
		attribution:
			'Woosh by 1bob -- https://freesound.org/s/831936/ -- License: Creative Commons 0',
	},
	// ... 共 32 条音效条目
];

for (const sfx of soundEffects) {
	copyFileSync(sfx.fileName, path.join(outDir, sfx.fileName));
	const stat = await Bun.file(path.join(outDir, sfx.fileName)).stat();
	variants.push({
		videoCodec: 'none',
		audioCodec: 'pcm',
		container: 'wav',
		fileNames: [sfx.fileName],
		size: stat.size,
		category: 'sound-effects',
		attribution: sfx.attribution,
	});
}

可以看到:每条 soundEffects 记录只要求 fileNameattribution(格式统一为 描述 by 作者 -- 来源链接 -- License: Creative Commons 0),脚本会用 copyFileSync 把 WAV 复制到 files/ 输出目录,并把 sizeattribution 等元数据追加进 variants.json(见 variants.json)。当前该数组已收录 32 个音效,packages/remotion-media 目录下也能找到对应的 whip.wavvine-boom.wavrecord-scratch.wav 等源文件。

七步工作流详解

步骤 1:向 remotion.media 仓库登记(必须最先做)

操作顺序不可颠倒,具体为:

  1. 将 WAV 文件放到仓库根目录;
  2. generate.tssoundEffects 数组中新增条目:
{
  fileName: "my-sound.wav",
  attribution:
    "Description by Author -- https://source-url -- License: Creative Commons 0",
},
  1. 运行 bun generate.ts,触发文件复制到 files/ 并重新生成 variants.json
  2. 部署(deploy)使 https://remotion.media/my-sound.wav 可公网访问。

这一步的意义在于:@remotion/sfx 导出的是远程 URL 字符串而非文件本身,若素材没有先部署上线,后续所有文档页的试听按钮、波形采样都会失败。

步骤 2:在 packages/sfx/src/index.ts 中新增导出

打开 packages/sfx/src/index.ts,按既有模式追加一行:

export const mySound = 'https://remotion.media/my-sound.wav';

两个命名约束值得注意:

  • 变量名统一使用 camelCase
  • 规避 JavaScript 保留字,例如文件叫 switch.wav 时导出名应为 uiSwitch

从源码看,该文件的 32 个导出全部采用 export const xxx = 'https://remotion.media/xxx.wav' as const; 的形式——as const 字面量断言保证了类型系统里得到的是精确字符串字面量类型而非宽泛的 string,这让 <Audio src={whip} /> 这类用法在 IDE 中能获得精确提示。其中 uiSwitch(对应 switch.wav)正是保留字规则的实际案例。

步骤 3:创建文档页 packages/docs/docs/sfx/<name>.mdx

以现存的 whip.mdx 为标准模板,新页面必须包含以下完整结构:

---
image: /generated/articles-docs-sfx-whip.png
title: 'whip'          # 与 index.ts 中的 camelCase 导出名一致
crumb: '@remotion/sfx'
---

# whip<AvailableFrom v="4.0.429" />

import {PlayButton} from './PlayButton';

<PlayButton src="https://remotion.media/whip.wav" />
<br />

A URL pointing to a whip sound effect WAV file.

## Example

​```tsx twoslash title="MyComp.tsx"
import {whip} from '@remotion/sfx';
import {Audio} from '@remotion/media';

const MyVideo = () => {
  return <Audio src={whip} />;
};
​```

## Value

​```bash
https://remotion.media/whip.wav
​```

## Duration

0.173 seconds (2 channels, 96000 Hz, 24-bit)

## Attribution

SWSH_Badminton Racquet_Recording_01_JW Audio by JW_Audio -
[freesound.org/s/838766](https://freesound.org/s/838766/) - License: Creative Commons 0

## Convert

[Open this file on remotion.dev/convert](https://remotion.dev/convert?url=https%3A%2F%2Fremotion.media%2Fwhip.wav)

## See also

- `whoosh`

各小节职责:

  • Frontmatterimage 为自动生成的封面图、title 必须等于导出名、crumb 固定为 '@remotion/sfx' 用于面包屑导航;
  • <AvailableFrom> 标签:标注该音效自哪个版本可用,取值为"下一个发布版本"(见步骤 7 前的版本规则);
  • <PlayButton>:来自同目录的 PlayButton 组件,让用户在文档内直接试听;
  • Example:使用 @remotion/media<Audio> 组件演示实际用法;
  • Duration:需下载文件后用 macOS 的 afinfo 命令获取时长、声道数、采样率与位深(whip 为 0.173s / 双声道 / 96kHz / 24-bit);
  • Convert:一条带完整 URL 编码的深链接,指向 remotion.dev/convert 转换服务;
  • See also:关联其他音效页形成内链网络。

步骤 4:注册侧边栏与目录

两处必须同步登记,否则新页面在文档站中"不可见":

  1. packages/docs/sidebars.ts(约 L712–L746)——在 @remotion/sfx 分类下追加 'sfx/<name>'。当前该分类共列出 32 个条目,从 sfx/whip 一直排到 sfx/record-scratch
  2. packages/docs/docs/sfx/table-of-contents.tsx——新增一个 <SfxItem>(底层是 <TOCItem>),其中 <PlayButton size={32}> 会在目录卡片上渲染一个 32px 的播放按钮,name 属性填 camelCase 导出名(注意条目 link 用 kebab-case 文件名的 URL 形式,如 ui-switch,而 name 显示为 uiSwitch)。

步骤 5:重新生成 SFX 波形

bun packages/docs/generate-sfx-waveforms.ts

这一步常被忽略,但它是文档站试听体验的关键。查看 generate-sfx-waveforms.ts 的实现可知其完整管线:

  1. 用正则 /export const\s+([a-zA-Z0-9_]+)\s*=\s*'https:\/\/remotion\.media\/([^']+\.wav)'/gpackages/sfx/src/index.ts 的源码文本中解析出所有导出(若一个都解析不到会直接抛错,这也解释了为什么步骤 2 必须先行);
  2. 对每个音效调用 ffmpeg -i <file> -ac 1 -f f32le -,从本仓库 packages/remotion-media 目录读取同名 WAV(缺文件同样抛错,印证了步骤 1 必须先完成);
  3. 将 PCM 均分为 2000 个桶,逐桶取绝对值峰值,再按全局最大峰值归一化到 0–255 的字节序列;
  4. 以 base64 编码写入 sfx-waveforms.ts,导出 SFX_WAVEFORM_SAMPLE_COUNT = 2000sfxWaveforms 映射表。

也就是说:新增一个音效而不重新采样,文档站的波形展示就会缺失该条目。

步骤 6:更新 Skills 规则文件

packages/skills/skills/remotion-markup/sfx.md 的音效 URL 列表中追加新 URL。该文件是 Remotion 面向 AI 编码助手的标记规则(skill rule),内容即 32 个 remotion.media 音效 URL 的清单,并附带用 <Audio src={...} /> 的示例写法。同步更新它可保证 AI 助手在生成含音效的 Remotion 代码时能识别新加入的素材。

步骤 7:构建

cd packages/sfx && bun run make

对照 packages/sfx/package.json 的 scripts 定义,make 实际展开为:

tsgo && bun --env-file=../.env.bundle bundle.ts

即先用 TypeScript 原生编译器(tsgo)产出 dist/index.d.ts 类型声明,再用 bundle.ts 打包 ESM 产物 dist/esm/index.mjsexports 字段中 import/module 均指向该文件)。--env-file=../.env.bundle 表明 bundle 过程依赖 monorepo 根部的 .env.bundle 环境变量——这正呼应了前置条件中"worktree 里可能缺少 .env"的提示。

命名规范

技能文档给出了文件名到导出名的映射约定,直接对照源码中的真实例子:

文件名 导出名 说明
my-sound.wav mySound kebab-case 转 camelCase
switch.wav uiSwitch 规避 JS 保留字
page-turn.wav pageTurn 常规映射

源码中可找到全部对应关系:export const pageTurn = '.../page-turn.wav'export const uiSwitch = '.../switch.wav'export const mouseClick = '.../mouse-click.wav' 等,规则无一例外。

版本号规则

  • 当前版本以 packages/core/src/version.ts 中的 VERSION 为准(当前为 4.0.520,与 packages/sfx/package.json 的 version 字段一致);
  • 文档页 <AvailableFrom> 的取值为当前补丁号 + 1,因为新音效必然随下一个补丁版本发布。例如 whip.mdx 标注 <AvailableFrom v="4.0.429" />,说明它是在 4.0.429 首次发布的音效。

全链路依赖关系小结

把 7 个步骤串起来,可以看到一条严格的先后依赖链:

  1. generate.ts 登记 + 部署 → remotion.media 上 URL 可访问(素材层);
  2. packages/sfx/src/index.ts 导出 → 成为一切下游的解析入口(库层);
  3. 文档页 mdx + sidebars.ts + table-of-contents.tsx → 用户可见(文档层);
  4. generate-sfx-waveforms.ts 采样 → 文档站波形数据(展示层,且强依赖步骤 2 与 packages/remotion-media 中的本地 WAV);
  5. sfx.md 规则清单 → AI 助手可用(工具链层);
  6. bun run make → 类型与 ESM 产物就绪(发布层)。

任何一环遗漏(尤其是步骤 5 的波形重采样和步骤 6 的 skills 清单),都会造成"包已发布但文档站试听/波形缺失"或"AI 无法感知新音效"的不一致状态。理解这条链路后,添加新音效就不再是零散的 checklist 背诵,而是一次有明确数据流向的多包协作操作。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
983
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384