跳转至

Voice Gallery — 设计与实现规格

日期: 2026-05-31 分支: debpalash/voice-gallery 状态: 已批准(设计),实现中 发布版本: v0.3.0(依据 CLAUDE.md 版本命名规则 — 无版本闲聊)


1. 目标

一个可浏览的设计声音原型库 — 数百种即用型声音, 全部由 VoiceStudio 自身的声音设计引擎生成 — 采用 ElevenLabs 风格组织(顶部精选卡片,分面筛选以探索其余内容), 外加一个中立的 URL/文件导入器,供用户自带源音频。

项目作者零名人 / 具名真人目录。每个原型的“氛围” 来自 token 组合 + 示例脚本 + 策展式命名,绝不源于克隆某个可识别个体。

为何没有名人目录(决策记录)

用户粘贴的 URL 导入器是中立基础设施(类似 yt-dlp/ffmpeg)—— 用户提供来源并自行承担许可责任。但项目随附的 具名真人目录(名人 / 政客 / 迪士尼 / 漫威)是一种编辑行为,它:

  • 将项目从中立工具转变为诱导 / 帮助侵权责任 (MGM v. Grokster 案一路线),
  • 让维护者的名字暴露在形象权 / ELVIS-Act / NO-FAKES 风险之下,
  • 是当前语音诈骗浪潮的主要推手。

因此:保留中立的导入器,删除策展式名人分类,以 合成原型为先。


2. 绑定的引擎约束

omnivoice/utils/voice_design.py 定义了完整的已验证指令 词汇表。model.generate(instruct=...) 运行 _resolve_instruct, 该函数会拒绝其中任何 token 并抛出 ValueError(这曾导致 issue #89)。 完整表达调色板:

轴 有效 token 排他性
性别 male、female 单选
年龄 child、teenager、young adult、middle-aged、elderly 单选
音高 very low pitch、low pitch、moderate pitch、high pitch、very high pitch 单选
风格 whisper(唯一风格 token) 可选
口音(仅限 EN) american、british、australian、chinese、canadian、indian、korean、portuguese、russian、japanese 单选
方言(仅限 ZH) 河南话、陕西话、四川话、贵州话、云南话、桂林话、济南话、石家庄话、甘肃话、宁夏话、青岛话、东北话 单选

关键点: 不存在情感/描述性风格轴(没有像 ElevenLabs descriptive 那样的 “calm”、“raspy”、“warm”)。我们无法 1:1 导入 ElevenLabs 声音。我们借用其结构(用例类别 + 分面筛选器)和 人口统计分面(年龄/性别/口音直接映射),但不借用其风格 词汇表。口音(EN)和方言(ZH)互斥 — 绝不在同一条 instruct 中组合。

规模(我们如何达到“数百”)

合法英语空间 ≈ 性别(2) × 年龄(5) × 音高(5) × 口音(11 含中性) ≈ 550,经 whisper 修饰符后约翻倍。修剪至合理组合 → 约 300–500 个英语原型。中文方言空间(性别 × 年龄 × 音高 × 12 种方言)再增加数百个。这通过人口统计/嗓音多样性而非 情感多样性,达到 ElevenLabs 级别的广度。


3. 分类法(取代名人/政客/迪士尼/漫威)

七个用例类别(对标 ElevenLabs,不含真人):

narration 叙述与故事 · conversational 对话 · characters 角色与动画 · social 社交媒体 · entertainment 娱乐与电视 · advertisement 广告 · informative 信息与教育

分面筛选器: 性别 · 年龄 · 音高 · 口音 · Whisper · 语言(EN / ZH)。

精选原型带有策展式、人工分配的 use_case。浏览全部 原型则获得从分面派生的启发式主 use_case(文档中标注为近似值), 因此用例筛选在整个集合中仍然有效。


4. 架构

4.1 信息架构

Gallery 标签页有两个区域(顶部切换):

  • Archetypes(原型)
  • Featured(精选) — 约 24 个精选原型,预渲染预览 WAV,按用例分组。
  • Browse all(浏览全部) — 生成数百个集合;可进行分面筛选;预览按需渲染 + 缓存。
  • My Imports(我的导入) — 中性 URL/文件导入器(复用现有 yt-dlp + AudioTrimmer 流程),无策展类别。

4.2 原型模型(值对象 — 非 DB 行)

{
  "id": str,            # 稳定:短哈希(instruct + language)
  "name": str,          # “British · Middle-aged · Low — Narrator”(生成)或策展命名
  "icon": str,
  "use_case": str,      # 7 个 id 之一
  "instruct": str,      # 逗号连接的合法 token — 保证通过验证器
  "attrs": {Gender, Age, Pitch, Style, EnglishAccent, ChineseDialect},  # 驱动设计滑块
  "facets": {gender, age, pitch, accent, whisper, lang},                # 驱动筛选器
  "sample_script": str,
  "preview_url": str | None,   # 精选项已设置(预渲染);None => 按需渲染
  "is_featured": bool,
  "language": "English" | "Chinese",
}

4.3 后端

  • 新增 backend/core/archetypes.py
  • USE_CASES、FEATURED(约 24 个,扩展 personalities 模式)。
  • generate_archetypes() — 遍历验证器自身的集合,通过 规则表修剪不合理组合,自动命名,分配启发式用例,构建 保证有效的 instruct 字符串。确定性顺序 + 稳定 ID。
  • list_archetypes(filters)、get_archetype(id)、categories()。
  • 从 omnivoice/utils/voice_design.py 导入词汇表(单一事实来源)。
  • 新增 backend/api/routers/archetypes.py
  • GET /archetypes/categories
  • GET /archetypes?use_case=&gender=&age=&pitch=&accent=&whisper=&lang=&featured=&limit=&offset=
  • GET /archetypes/{id}
  • GET /archetypes/{id}/preview — 若存在预渲染 WAV 则直接提供;否则通过 声音设计引擎渲染并按 instruct 哈希键缓存到磁盘。
  • POST /archetypes/{id}/use — 渲染示例 → 创建 voice_profile (渲染 WAV 作为 ref_audio,原型的 instruct/language)→ 返回 profile id。
  • 在 backend/main.py 中与其他 router 一并注册。
  • 预览缓存 — OUTPUTS_DIR/archetype_previews/<hash>.wav,通过新的 静态挂载或 FileResponse 提供。预渲染的精选 WAV 位于 backend/assets/samples/voice_design/ 下(通过 /demo_audio 现有挂载提供)。
  • 导入器(复用 gallery.py) — 删除名人 CATEGORIES 常量; voice_gallery 表现在仅支持 “My Imports”。/gallery/search/youtube 保留,但由用户提供的 URL/查询驱动(无名人默认值)。不添加 年龄门控 cookie 绕过。

  • 约束: POST /profiles 要求 ref_audio,因此“使用原型” 必须先渲染 WAV — 在 /archetypes/{id}/use 内处理。


5. 渲染管线(JSON → WAV)

5.1 架构 / 流

前端点击原型中的 ▶ 会提示后端渲染。完整流程:

  1. Browser – 获取 GET /archetypes/{id}/preview;返回 {wav_url, cached}。 若 cached == false,则触发 PUT /archetypes/{id}/preview/render(幂等), 然后轮询 wav URL,直到 200 或出现错误。
  2. Backend preview – 检查磁盘缓存 → 若非空则返回 FileResponse。 若缓存未命中,返回 202 和 {wav_url: "/preview/{id}"}(客户端 负责触发渲染并轮询)。
  3. Backend render – 锁 + 原子重命名。空闲时渲染所需 GPU 时间约为 2–10 秒,具体取决于序列长度。

5.2 缓存语义

  • 确定性输入:缓存键为原型 instruct + language + voice_design 引擎 版本。
  • voice_design.py 的 engine-version bump ⇒ 缓存失效。发布说明 注明 “voice_design v2 ⇒ 清空 OUTPUTS_DIR/archetype_previews”。
  • 新旧键并存在磁盘上:旧的 WAV 会保留,但永远不会被 读取(列表按前缀枚举)。隐含容量上限:8 GB, 由磁盘使用率扫描处理,删除最旧条目。

5.3 渲染限制 (Lock + 常量内存运行)

默认情况下,任何渲染请求最多使用一个 GPU seconds。实际内存 占用通过对输出 strength 设置上限并调用 del + gc.collect() 来限制。单个共享进程范围锁保护全部渲染端点 (/profiles、/archetypes/*/preview、/archetypes/*/use — 但不是 ASR 转录),这样 3 个并发预览不会同时占满 3 个 GPU。


6. 前端

6.1 技术栈

  • 与 StoriesEditor/AudiobookTab 相同的 UI 技术栈。
  • Gallery 是第 4 个标签页。

6.2 路由 / 组件

  • /gallery — 顶层切换器。
  • /gallery?tab=archetypes(默认)
  • 部分 1:Featured — 水平滚动、卡片式、按 use_case 分组。
  • 部分 2:Browse all — 分面筛选器 + 虚拟化网格。

  • /gallery?tab=imports(现状:无策展;保留搜索/导入)

新 UI 组件:

  • FacetFilterBar — 固定顶部;gender、age、pitch、accent、 whisper、lang。联动 — 不匹配的选项自动失去可选性。
  • ArchetypeCard — 图标、名称、样本脚本工具提示(英文/中文字符串)、 ▶ 渲染带旋转图标和失败状态的按钮。
  • ScrollToTopButton — 网格超大时的浮动返回顶部按钮。

可访问性: 卡片为 <button> 元素,完整键盘支持(箭头键 = 虚拟化网格导航),“渲染”事件通过 aria-live 播报。


7. API 参考

7.1 端点

原型

  • GET /archetypes/categories → {use_cases: [{id, label, count}]}
  • GET /archetypes?use_case=&gender=&age=&pitch=&accent=&whisper=&lang=&featured=&limit=&offset= → {total, items: [ArchetypeSummary]}
  • GET /archetypes/{id} → ArchetypeDetail
  • GET /archetypes/{id}/preview → FileResponse(缓存命中)或 202 {wav_url}
  • POST /archetypes/{id}/render → 202 {status: "pending"}
  • POST /archetypes/{id}/use → {profile_id}

导入

  • POST /gallery/import(现有流程;不新增端点)
  • GET /gallery/search/youtube(现有流程;请求时由用户输入驱动)

模型

  • ArchetypeSummary — id、name、icon、use_case、language、preview_url
  • ArchetypeDetail — Summary + instruct、attrs、facets、sample_script

8. 数据与持久化

  • 后端 未配置新 DB:原型 === 模块级常量。
  • 仅画廊的 “My Imports” 表(已有 voice_gallery 表)使用 sqlite3。
  • 迁移: 无。旧的 celebs 种子文件仅存于 backend/assets/samples/voice_design/,永久移除(改用 精选原型目录)。

9. 测试

层 文件 内容
单元测试 backend/core/archetypes_test.py 每个枚举组合成功;x(No Y) 对于 whisper/language 组合抛出 ValueError;ID 的确定性与稳定性;预览哈希针对击键注入进行测试。
API 测试 backend/api/routers/archetypes_test.py 过滤器工作正常;分页返回正确的 total 与 items 长度;preview 缓存命中/未命中均正确返回。
安全测试 backend/security ReDoS 防护测试:分面查询在硬编码拒绝列表中的恶意模式后,日志标记为 deny;不直接针对 LLM 测试。
冒烟测试 CI 生成 50 个 archetype → 渲染到 WAV → 解码。
回归测试 UI Gallery 负载 ≤ 2.0 s,筛选交互 ≤ 300 ms,GPU 在 use 渲染期间不超过 4 GB。

10. 性能 / 规模预算

操作 预算
启动时原型生成 后台 · ≤ 2 s(生成数百个常量)。
列表 + 分面筛选响应 ≤ 300 ms(在 Python 中过滤常量列表;SQL 仅用于导入)。
磁盘缓存 ≤ 8 GB(启用清理;超过上限删除最旧的 WAV)。
初次原型渲染 ≤ 10 s(典型值 2–5 s)。
并发 每个进程 1 个渲染(进程范围锁)。

11. 安全与隐私

  • 仅支持合成人声:根据项目可见性规则,无克隆模式。
  • 用户导入需自行同意(现有流程)。库自身不再包含其 装饰性头像与姓名为虚构角色配音的人工智能生成条目的 “名人库”种子。
  • 保存到磁盘的每个项目现均在编辑会话结束时销毁 — 预览缓存保存的 WAV 不含端到端标识符,文件名基于哈希。
  • WAV 不会嵌入到画廊目录中。预览是 PROTECTED 媒体 — 不允许记住 cookie。
  • 在实现过程中不做任何变更,而旧文件 gallery.py 继续同时服务于“导入”功能(修订 Celeb 列表调用等)—— 仅删除 celebs 固定预设。

12. 发布说明(面向用户)

Voice Gallery 已上线,全部通过 VoiceStudio 自家引擎合成。 浏览激发灵感的分类与嗓音原型,试用不同口音、年龄和 音高 — 全部合成,零名人预设。自带任何音频,通过 中性导入器,自行负责许可。合成声音 + 你的创意,不被 冒充所玷污。使用基于文件的脚本,在上线前预览每个原型。


13. 超出范围(后续发布)

  • 每个原型的沙盒预览脚本,整合至后续版本发布说明中
  • 按需自定义:用户驱动的 “混合” 声音 fader
  • 从用户导入的 WAV 中派生元数据(自动描述)
  • “每日原型” 刷新计划(用户给定日期 → 合成亮灯)
  • 未来支持其他语言的方言标签
  • “我的收藏” 持久化存储 — 除免费层外,仅本地存储
  • 原型 sitemap 为 robots.txt 提供 SEO 条目(除 robots.txt 外)

14. 决策记录 (ADR)

ADR-01:为什么是原型,而不是可写样式快照

原型只是预设 —众多预设构型(性别 × 年龄 × 音高 × 口音/方言, 可选 whisper),由指令集定义并内置其中。用户还需要 “软” 版本 — 包含用户声音的参数,以及其对文本的影响 — 但这属于另一个工作项,暂不做。

ADR-02:为什么十一要素风格的结构(案例 → 筛选器)

刮削 “浏览” 页面,散落的 “千面”。这改变了他们的目录页面 — 来自每个用例、人口统计和风格滤波器的数据,一目了然。我们 使用相同的信号形状。

ADR-03:为什么不对 ent/actor 姓名进行预训练

已争论过。语音提示调用的行为模式(同一系统、整条提示的 几个 token)与直接合成其人声的姓氏在同等程度上暗示了 名人生成。这令我们更接近诱导责任线,而不是 远离它 — 正如边界一样。

ADR-04:独立 UI 和缓存优先于 DB 行

一个固定目录上没有 DB 行意味着更严格的封装。

ADR-05:通常情况下,唯一语言模型是遗嘱

在预设目录界面驱动下使用与数据库相同的设计语言 — 引擎自身负责。画布入口加至界面推出的音量配置 —— 保留其演变的可运行性。

ADR-06:与音乐审查相关的扩展

在 V 上为项目自身的声音中添加有谱的歌唱和旋律脚本。从 同一模型包推送记忆和谐的叙事。

4.4 前端

  • 重写 frontend/src/pages/VoiceGallery.jsx — 分区切换、分面筛选栏、精选网格、带懒加载预览的浏览网格、卡片操作。移除名人 CATEGORY_ICONS 映射。
  • 新增 frontend/src/api/archetypes.ts 以及 frontend/src/api/hooks.ts 中的 hooks(useArchetypeCategories、useArchetypes(filters)),遵循现有的 react-query queryKeys/useQuery 模式。
  • 新增 frontend/src/store/gallerySlice.ts — 收藏的原型 ID(持久化)、当前筛选条件、当前分区、视图模式。注册于 store/index.ts。(原型不在数据库中,因此收藏保存在客户端。)
  • 卡片操作: 预览(播放 WAV)· 使用语音(主要操作 → POST /archetypes/{id}/use → 保存为 profile,随处可用)· 在 Designer 中打开(次要操作 → setInstruct + setVdStates + 切换到 Design 标签页)· 收藏。

4.5 国际化

所有新字符串 → archetypes.* 和现有 gallery.* 键;en.json 为权威文件,在翻译完成前,其余 20 个语言环境回退到英语。中文方言显示名称属于功能性模型词汇——将其加入 test_no_hardcoded_cjk.py 的允许列表并说明理由,不要在其他地方内联。


5. 已确定的决策

  1. 目录方案:C(混合式) — 约 24 个精选条目 + 生成的浏览全部列表。
  2. 分类:特质/用例,取代所有以真实人物命名的类别。
  3. 卡片主要操作:使用语音(保存为 profile);Designer 为次要操作。
  4. 浏览全部预览:按需渲染 + 磁盘缓存(不预渲染数百个)。
  5. 精选预览:通过 render_demos_omnivoice.py 预渲染 WAV。

6. 跨平台 / 约束合规(CLAUDE.md)

  • 默认功能对等(严格): 原型浏览 + 按需预览 + “使用语音”在 macOS/Windows/Linux 上行为一致(纯 Python 合成 + 静态资源——无平台相关代码)。yt-dlp 导入器是唯一的平台可变暴露面 → 以状态形式呈现;如果缺少 yt-dlp/ffmpeg,导入器会优雅降级。默认行为不会因平台而不同。
  • 向后兼容的数据: 不对 voice_profiles 做 schema 变更;voice_gallery 表原样复用。无需 alembic 迁移。现有 profiles/导入不受影响。
  • 本地优先: 除用户主动发起的导入器下载外,无网络调用。
  • 版本管理: 随 v0.3.0 发布;不涉及版本讨论。

7. 分阶段实施计划

阶段 1 — 原型引擎(TDD)。 backend/core/archetypes.py + backend/tests/test_archetypes.py。先写测试:(a) 每个生成的 instruct 仅由验证器 _INSTRUCT_ALL_VALID 集合中的 token 组成,并遵循每类别唯一性;(b) 口音和方言不会同时出现;(c) 生成数量在数百级别;(d) 不合理的组合被剪除(不允许 child + very low pitch,不允许 elderly + very high pitch);(e) ID 唯一且跨运行稳定;(f) 精选条目有效且包含 use_case + script。

阶段 2 — 原型 API。 backend/api/routers/archetypes.py + 注册到 main.py + 预览缓存目录。测试:分类、列表 + 每个筛选条件、分页、404、针对预渲染精选 id 的 /preview、/use 正常路径(受模型门控/在缺少权重时跳过)。

阶段 3 — 去名人化导入器。 从 gallery.py 和前端中的 'celebs'/'famous voice' 默认值中移除名人 CATEGORIES;保留上传/裁剪/另存为 profile。更新受影响的测试。

阶段 4 — 前端数据层。 api/archetypes.ts、hooks、store/gallerySlice.ts,注册 slice。对 slice/util 进行类型检查 + 单元测试。

阶段 5 — 前端 UI。 重写 VoiceGallery.jsx:分区切换、分面栏、精选 + 浏览网格、懒加载预览、卡片操作、My Imports 导入器。

阶段 6 — i18n。 向 en.json 添加 archetypes.* 键;确认允许列表之外没有硬编码 CJK。

阶段 7 — 精选预览资源。 扩展 render_demos_omnivoice.py 以渲染约 24 个精选 WAV(开发机步骤,固定种子)。如果某个 WAV 缺失,精选项回退到按需渲染。

阶段 8 — 验证。 后端 pytest、前端 typecheck/test/lint、test_no_hardcoded_cjk.py、对图库标签页进行手动冒烟测试。


8. 不在范围内

  • 项目自建的名人/角色目录。
  • 用于抓取的自动年龄门槛 / cookie 绕过。
  • 情感风格 instruct token(引擎不支持)。
  • GitHub 应用自动提交 bug 报告的路径(无关)。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 om/VoiceStudio