Dev Spec v2 · ← 返回文档首页

02 系统设计

1. 模块

products/sonicflow/
├── works/          # 作品:源文件 + 参数 + 渲染版本(非破坏性)
├── recipes/        # 配方:sing_full / talk_clean / talk_edit / tool_*(步骤列表 + 默认参数)
├── runner.py       # 执行配方:把每一步投递到 CPU / GPU 队列,串起中间文件
├── presets/        # 曲风预设(人声链 + 混音比例)、响度预设
├── quota.py        # 免费额度、订阅权益(读 core.entitlements)
├── tools/          # 免费工具箱:pitch_score / separate_lite / transpose / key_bpm
└── api/
media/audio/steps/  # 处理步骤(见 04);与 01 共用语音能力
media/video/        # 歌词视频(见 06)

2. 作品(work)与渲染(render)

  • 作品:一次创作。保存源文件、配方、参数(曲风、修音强度、歌词……)
  • 渲染:作品按某组参数跑出来的一个结果(音频 + 视频)。修改参数后产生新的渲染;免费用户付费后,可以对旧作品重新渲染出无水印版本

2.1 作品状态

draft → uploaded → processing → ready → exported
             ▲            │
             └── 改参数 ──┘         processing 失败 → failed(退回额度)

2.2 步骤缓存

每一步的输出按 (步骤名, 步骤版本, 输入哈希, 参数哈希) 缓存 7 天。用户只改了混音参数时,前面的分离、修音不需要重跑。

3. 数据库(schema sonicflow)

CREATE TABLE sonicflow.works (
  id TEXT PRIMARY KEY, user_id TEXT, anon_id TEXT, region TEXT NOT NULL,
  mode TEXT NOT NULL,                     -- sing|talk
  recipe TEXT NOT NULL,                   -- 'sing_full@1'
  title TEXT,
  params JSONB NOT NULL,                  -- {"genre":"pop","tune_strength":0.7,"reference":"original"|"key","key":"A minor", ...}
  sources JSONB NOT NULL,                 -- {"vocal":"med_..","backing":"med_..","original":"med_.."}
  lyrics JSONB,                           -- 用户粘贴的歌词 / 识别结果(带时间戳)
  status TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sonicflow.media (
  id TEXT PRIMARY KEY, owner_id TEXT NOT NULL,       -- work id
  role TEXT NOT NULL,                     -- vocal|backing|original|stem|intermediate|output|video
  object_key TEXT NOT NULL, kind TEXT NOT NULL,      -- audio|video|image|text
  duration_s REAL, probe JSONB,
  purge_after TIMESTAMPTZ NOT NULL        -- 源文件:免费 7 天 / 付费 90 天(付费时自动延长);中间文件 7 天
);

CREATE TABLE sonicflow.renders (
  id TEXT PRIMARY KEY, work_id TEXT NOT NULL,
  params_hash TEXT NOT NULL,
  watermark BOOLEAN NOT NULL, resolution TEXT NOT NULL,   -- '720p'|'1080p'
  backing_source TEXT NOT NULL,           -- licensed|original_separated|none(清唱)
  outputs JSONB NOT NULL,                 -- licensed/none:{"mp3","wav","video","stems"};original_separated:只有 {"vocal_safe":"med_..","offset_ms":..}
  metrics JSONB NOT NULL,                 -- 修音前后偏差、响度、真峰值……
  cost_usd NUMERIC(8,5),
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sonicflow.jobs (
  id TEXT PRIMARY KEY, work_id TEXT NOT NULL, step TEXT NOT NULL, step_version TEXT NOT NULL,
  queue TEXT NOT NULL, params JSONB NOT NULL, cache_key TEXT,
  status TEXT NOT NULL, metrics JSONB, error TEXT,
  cpu_seconds INT, gpu_seconds INT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(), finished_at TIMESTAMPTZ
);

CREATE TABLE sonicflow.usage (             -- 额度消耗
  user_id TEXT NOT NULL, period TEXT NOT NULL,          -- '2026-10'
  songs INT NOT NULL DEFAULT 0, talk_minutes INT NOT NULL DEFAULT 0,
  tools JSONB NOT NULL DEFAULT '{}',
  PRIMARY KEY (user_id, period)
);

CREATE TABLE sonicflow.tool_runs (         -- 免费工具使用(匿名可用)
  id TEXT PRIMARY KEY, anon_id TEXT, user_id TEXT, tool TEXT NOT NULL,
  result JSONB, share_key TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

4. 配方

配方 步骤
sing_full@1 probe → [separate(original)] → [separate(vocal,若上传的人声混有伴奏)] → denoise(vocal) → pitch_track(vocal, ref) → align → tune → [timing] → vocal_chain → mix → master → lyrics → video → encode → label
talk_clean@1 probe → denoise → leveler → loudnorm → encode → label
talk_edit@1 talk_clean 的前三步 → transcribe → detect → auto_accept(level) → render → loudnorm → subtitles → [video] → encode → label
tool_pitch_score@1 probe → [separate(original)] → pitch_track × 2 → align → score

5. 额度与权益

权益键(core.entitlements) 免费 Pro
songs_month 3 30(公平使用,超出后排队降低优先级)
video_res 720p 1080p
watermark true false
wav_export、stems_export false true
vocal_safe_export true true(原曲伴奏作品唯一的导出方式)
source_retention_days 7 90
tool_separate_day 3(128kbps) 20(无损,4 轨)
talk_minutes_month(Creator) 30 480

作品失败时不扣额度(或自动退回)。

6. 定时任务

任务 频率
purge 每小时:删除过期的媒体文件
quota_period_roll 每月 1 日
regression 每周:用测试曲跑一遍全部配方,分数下降就告警
gpu_watch 每分钟