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 |
每分钟 |