PianoReel · 项目状态(单一信源)
一页讲清:目标、现在的进度、产品最终形态。其它文档:架构看
ARCHITECTURE.md,形态发散看 14-formats-and-vision.md,
YouTube 导入看 13-youtube-import.md。最后更新:2026-06-07。
1. 目标
一句话:让任何人把一首歌一键变成可直接发布的竖屏钢琴短视频,并能下载
歌曲、谱子、封面。
- 目标用户:钢琴演奏者、音乐老师、短视频/音乐内容创作者。
- 目标平台:YouTube Shorts、TikTok / 抖音、视频号、B站。
- 核心主张:降低门槛(不用懂 MIDI/编曲/剪辑)+ 出片够"优秀、花样多、有吸引力"
(对标 Rousseau / Synthesia 主流格式,并叠加歌词/原视频/氛围等模式)。
- 可交付物(每首歌):MP4 视频 · 谱子(PDF/MusicXML) · 高音质音频(MP3) · MIDI · 封面 PNG。
2. 现在的进度
阶段:本地端到端闭环已跑通;上线阶段 1 完成(前端已部署公网)。 后端上云 / 账号 / 付费未开始。
🌐 已上线(阶段 1):前端静态站部署到 Cloudflare Workers 静态资源,域名 pianoreel.com(Cloudflare Registrar 注册)。
开发/预览地址 dev.pianoreel.com(已绑定,每次 push 自动重部署);主域 pianoreel.com 留给正式发布;底层 pianoreel.zhukui83.workers.dev。
线上 config.js 自动判定为"无后端":示例曲/MIDI/全部特效/导出可用;MP3·YouTube·歌词等需等阶段 2 后端上 Modal。部署配置见 wrangler.jsonc,步骤见 docs/17。
✅ 已完成(可用并验证)
输入
- 上传 MP3 / WAV / M4A / MIDI
- 粘贴 YouTube 链接 → 自动下载音频 → 直接出片(≤8min,本地)
编曲
- Pop2Piano 本地模型:整首歌 → 双手可弹钢琴多轨(~15-60s)
- 21 种作曲风格、🔄 换一版(换风格重编)
- 实验路径保留:自研管线 / MiniMax LLM / pYIN 等
工作室(出片)
- 4 种出片模式(一键切):🎹 演奏 · 🌙 氛围 · 🎤 歌词 · 🎬 对比
- 🎤 歌词模式:Whisper 逐词时间戳卡拉OK高亮(双行);支持贴 LRC、选语言
- 🎬 对比模式:原视频分屏 / 背景两种布局(yt-dlp 下视频,本地/受限)
- 🌙 氛围模式:光斑+能量脉动+暗角(放松 BGM 场景)
- ➕ 片头标题卡(开头淡入淡出)
- 🌟 顶缘流光灯条 + 命中爆光(Rush E 风,所有 Look 自动带)
- 🖼 自定义背景纸:上传图片 / YouTube 模糊背景(演奏·歌词模式,可调模糊+压暗)
- 🔤 音名/唱名标签:C-D-E / Do-Re-Mi / ドレミ(日本固定唱名,面向日区)
- 竖屏 9:16 默认 + 16:9 / 1:1;13 种击键特效;8 主题;5 套"高级感"预设
- 🎯 副歌精华(自动定位高潮)、🎚 情感慢速(音高不变)
- 多轨混音(看/弹/音量)、加载人声(Demucs 4 轨叠加)
- 键盘快捷键(空格播放、←→快进退)
导出
- ⬇ MP4(H.264 / yuv420p / faststart,后端 ffmpeg 转码,全平台通用)
- ⬇ 谱子(music21 → PDF / MusicXML)
- ⬇ 高音质音频(FluidSynth + 真钢琴音色 → MP3)
- ⬇ MIDI、📷 封面 PNG
- 导出时长可选(整首 / 15 / 30 / 60s,从进度条位置截短视频)
工程
- 后端 FastAPI,按职责分包
engines / audio / services - 前端纯 JS(无打包),分
core / data / audio / visual ARCHITECTURE.md 架构地图;端点:`/arrange /youtube /youtube_video /separate
/lyrics /render /sheet /transcode /transcribe /health`
🔄 进行中 / 待打磨(非阻塞)
- 片尾卡、记住上次设置、更多氛围/特效变体
- 用真实歌做端到端成品验证(发频道测水温)
⏳ 未开始(商业化,需产品决策)
- 用户账号 / 登录、订阅付费 / 配额
- 部署上线(前端 Vercel;后端容器化;YouTube 线上需 cookies/住宅代理)
- 对象存储、异步任务队列、并发扩展
- 版权合规:原视频画面/歌曲版权——上线前的头号风险,需法务/授权策略
关键指标
| 指标 | 目标 | 当前 |
|---|
| 端到端闭环(上传→出片→下载) | 跑通 | ✅ 本地跑通 |
| 出片模式数 | 多样 | ✅ 4 模式 + 片头/封面 |
| 导出格式 | 视频+歌+谱+封面 | ✅ MP4/MP3/PDF/MIDI/PNG |
| 单曲出片耗时 | <2 分钟 | ✅ ~15-60s(编曲) |
| 商业化(账号/付费/部署) | 上线 | ⏳ 未开始 |
| 版权策略 | 明确 | ⚠️ 未解 |
3. 产品最终形态(北极星)
粘贴链接 / 上传一首歌 → 一条可直接发布的竖屏钢琴短视频。
不是"只有一种落音符动画",而是一个可叠层、可切模式的出片台:
[ 基底 ] 落音符 + 88 键 + 主题/预设 (招牌,已完成)
[叠层] 歌词卡拉OK · 片头/片尾卡 · 原视频(分屏/背景)
[调校] 竖屏 · 副歌精华 · 情感慢速 · 高级感预设
↓ 一键切 4 种模式
🎹 演奏(可弹/演奏感) · 🎤 歌词(传唱/情感) · 🎬 对比(反差/关注) · 🌙 氛围(放松BGM)
↓ 一键导出
MP4 视频 + 谱子 + 高音质音频 + 封面 —— 开箱即发
分发形态:双轨(见 docs/23)
- 🌐 网页版
pianoreel.com:只接受上传音频(干净、合规友好),后端在 Modal。已上线,MP3→钢琴 跑通。 - 💻 桌面 App(本地):全功能含 YouTube 下载(住宅 IP),后端本地。把 YouTube 的 IP/版权负担落到用户本机。未开始(现有
make dev 即其内核,差打包)。
完整形态还需补齐(商业化层):账号与作品库 → 订阅/配额 → 桌面打包 →
版权合规与(可选)授权曲库。技术内核已具备,缺的是"产品化 + 上线 + 合规"。
一句话总结
内核做完了(上传/链接 → 编曲 → 4 模式工作室 → 多格式导出,本地端到端跑通);
接下来是产品化与上线(账号、付费、部署、版权),这些需要你的方向决策。
01 · 商业方案 (Business Plan)
产品定名:PianoReel(「钢琴短视频,一键生成」)
命名考量:含 piano(SEO 易搜到 "piano visualizer")+ Reel(直指短视频/Reels,喊话创作者)。
一句话:上传任意音乐(MP3),自动生成「钢琴键盘随音乐演奏发光 + 音符下落」的高质量可视化视频。
1. 愿景与定位
把"音乐可视化视频"从专业门槛(需要 DAW、MIDI、After Effects、几小时手工)变成一键生成(上传 MP3 → 几分钟出片)。
对标产品的体验感:SeeMusic / Synthesia / PianoVision,但核心差异在于——
对手大多需要你先有 MIDI 文件,而我们解决的是"我只有一个 MP3 / 一段录音 / 一首歌"这个更普遍的入口。
定位句:
"Canva for music visualization" —— 不会编曲、不懂 MIDI 的人也能做出专业级的钢琴演奏视频。
业务边界(重要):我们是纯工具/服务——不提供曲库、不托管或分发音乐。
音源完全由用户自带(上传自己的 MP3/录音/MIDI)。这既是产品定位,也是版权风险的关键缓解(见 §6)。
2. 市场与竞品
2.1 目标市场
| 市场 | 描述 | 规模信号 |
|---|
| YouTube / B站 音乐区创作者 | 钢琴翻弹、Lo-fi、放松音乐、Cover 视频 | 钢琴教学/翻弹是 YouTube 长青品类,单频道百万订阅常见 |
| 短视频内容工厂 | TikTok / Reels / 抖音 上的音乐可视化内容号 | 批量产出、靠量变现,对自动化付费意愿强 |
| 钢琴学习者 / 老师 | 把曲子可视化辅助练习(类 Synthesia) | 全球钢琴学习是数千万级人群 |
| 音乐人 / 独立厂牌 | 给单曲做 lyric-video 的替代品(视觉化 MV) | 每首新歌都需要视觉物料 |
| 企业/B2B API | 音乐 App、教育平台想集成可视化 | 按调用付费 |
2.2 竞品对比
| 产品 | 输入 | 核心能力 | 痛点(我们的机会) |
|---|
| SeeMusic (Visual Musician) | 主要 MIDI / 部分音频 | 漂亮的可视化主题、导出视频 | 桌面软件、上手门槛、音频转写质量有限 |
| Synthesia | MIDI | 钢琴教学瀑布流 | 偏教学、视觉相对朴素、需 MIDI |
| PianoVision (VR) | MIDI | VR 弹琴 | 场景不同(硬件) |
| 各类 AE 模板 | 手工 | 完全可定制 | 极高人工成本 |
| 通用音频可视化 (如 Specterr) | MP3 | 频谱/波形动画 | 不是"演奏"语义,没有"哪个键被按下" |
我们的护城河方向:
- MP3 → 演奏语义(音频转写 / Audio-to-MIDI)这一最难的环节做到足够好 + 配套"轻量人工校正"体验。
- Web/App 原生、零安装、模板化——把对手的"专业软件"做成"消费级 SaaS"。
- 风格资产库(主题、粒子、配色、相机运镜)形成内容差异化和品牌。
3. 价值主张
- 省时间:从"几小时手工"到"几分钟自动"。
- 降门槛:不需要懂 MIDI / 编曲 / 视频软件。
- 多端:Web 创作 + App 随手分享,导出适配横屏/竖屏(YouTube vs TikTok)。
- 可商用质量:高分辨率、可去水印、风格统一。
4. 商业模式与定价
采用 Freemium + 订阅 + 按量 组合:
| 档位 | 价格(参考) | 内容 |
|---|
| Free | ¥0 | 720p、带水印、每月 N 首、基础主题 |
| Creator | ~¥39 / 月 | 1080p、去水印、全部主题、竖屏导出、更长时长 |
| Pro | ~¥99 / 月 | 4K、批量导出、音轨分离(左右手/人声)、自定义品牌、优先渲染队列 |
| API / B2B | 按调用计费 | 转写 + 渲染 API,给第三方集成 |
| 一次性 Credit 包 | 按片付费 | 不想订阅的轻度用户,按"出片数/分钟数"买点数 |
变现杠杆:
- 免费档水印 = 天然增长引擎(视频本身在传播你的品牌)。
- 4K / 批量 / 音轨分离 是清晰的付费墙。
- B2B API 是高毛利的第二曲线。
5. 成本结构(单位经济 Unit Economics)
每生成一个视频的可变成本主要是计算:
- 音频转写(MP3→MIDI):GPU/CPU 推理,几十秒~几分钟。
- 渲染出片:
- 方案 A(推荐 MVP):浏览器端实时渲染 + 客户端录制 → 服务器成本≈0(用户自己的 GPU 画)。
- 方案 B:服务器端离线渲染 MP4(headless + ffmpeg),成本高但质量/一致性可控、适合 4K/批量。
- 存储 + CDN:成品视频分发。
关键策略:免费/低档用客户端渲染把成本压到接近零;高档(4K/批量/服务器一致性)才用服务器渲染并据此定价。这让 Freemium 在经济上可持续。
毛利目标:订阅档 70%+;API 档按定价覆盖推理成本 + 合理 markup。
6. 关键风险
| 风险 | 说明 | 缓解 |
|---|
| 版权 ⚠️ 最大风险 | 用户上传受版权保护的音乐生成视频并传播 | ① 定位"纯工具/服务,我们不提供曲库、不托管/分发音乐,用户自带音源",责任在用户(ToS);② 个人使用导向;③ 对公开发布走 Content ID 友好路径;④ 引导用户使用自己创作/已授权的音乐 |
| 转写质量 | 复杂复调/带鼓的流行乐转 MIDI 难,易出错音 | ① 先聚焦钢琴/简单编制;② 提供"轻量人工校正"编辑器;③ 源分离(Demucs)先抽钢琴轨 |
| 成本失控 | 服务器渲染 4K 很贵 | 客户端渲染优先 + 队列 + 缓存 |
| 同质化 | 视觉效果被模仿 | 持续做风格资产库 + 社区 + 品牌 |
法律提醒:版权是这个生意的核心合规问题。上线前需要正式的 ToS / 版权政策,建议咨询专业律师;本仓库文档不构成法律意见。
7. Go-To-Market
- 种子内容:自己用产品批量产出高质量视频,铺 YouTube/TikTok/小红书/B站,每条带水印导流。
- 创作者合作:找钢琴/音乐区中腰部创作者免费 Pro 换内容曝光。
- 模板/主题作为传播单元:每出一个爆款视觉风格 = 一波拉新。
- PLG:免费档无障碍出片 → 水印传播 → 自然转化付费去水印。
- B2B:在 PLG 跑通后,把转写+渲染能力封装成 API 卖给音乐/教育 App。
北极星指标:每周成功导出视频数(activated exports / week)。
8. 里程碑(商业视角)
- M0 原型(本仓库):验证"下落音符+键盘发光"的视觉与同步体验。✅
- M1 MVP:MP3→可视化→导出,闭环跑通,少量种子用户。
- M2 商业化:订阅 + 水印 + 主题库,开始付费。
- M3 规模化:服务器渲染 4K、移动 App、API。
详见 04-mvp-roadmap.md。
02 · 产品需求 (Product Spec)
产品定名:PianoReel(「钢琴短视频,一键生成」)。面向 YouTube / Shorts / TikTok / 抖音 / B站 创作者。
0. 创作者便利清单 (Creator Conveniences)
把"专业软件"做成"创作者顺手的工具"是核心竞争力。下面是已在原型实现与规划中的便利点。
已实现(原型工作室):
- 🎵 MP3 直接入口 —— 不用懂 MIDI / 编曲(最大门槛降低)
- 🎼 示例曲目即点即玩 + 拖拽上传 + MIDI 真实解析
- ⏳ 处理流程可视化 —— 上传后有"识别音符"的进度反馈,降低等待焦虑
- 🎨 12+ 弹奏特效,逐个点选实时预览(钢琴特效 + 音频特效两组)
- 🌈 一键换主题配色
- 📐 平台画幅一键切:16:9(YouTube) / 9:16(Shorts·抖音) / 1:1
- 🏷️ 标题叠加 + 💧 水印开关(免费版水印 = 增长引擎)
- ⬇️ 一键导出带声音的视频
规划中(按创作者价值排序):
- 🔁 多画幅批量导出 —— 一次出"横屏+竖屏"两版(投不同平台)
- 🧩 风格模板包 —— 一键套用"配色+特效+画幅+字体"组合(爆款风格)
- ✂️ 高光片段裁剪 —— 为 Shorts 自动/手动挑最精彩的 15–30s
- 🖼️ 背景图/视频、专辑封面、片尾关注卡(CTA)
- 🎤 歌词 / 和弦显示
- 🏢 品牌套件 —— 付费版用创作者自己的 Logo 替换水印
- 🚀 直接发布/分享 到 YouTube / TikTok
- 💾 项目保存与二次编辑
- 🤖 自动封面/缩略图生成
不做:曲库/内容分发。我们只提供工具/服务,音源由用户自带(见 01 商业方案)。
1. 用户画像 (Personas)
- 创作者 Coco:YouTube 钢琴翻弹号,想快速把自己的演奏录音变成漂亮视频,关注画质和风格、竖屏/横屏。
- 内容工厂 Max:批量做音乐可视化号,关注批量导出、模板、稳定性、成本。
- 学习者 Lily:想把喜欢的曲子可视化辅助练琴,关注准确度、左右手区分、慢放。
- 音乐人 Ben:给自己原创单曲做视觉物料,关注品牌定制、4K、独特风格。
- 开发者 Dev(B2B):想在自己 App 里集成,关注 API、SDK、文档。
2. 核心用户旅程 (Happy Path)
上传 MP3 ──▶ 自动转写(MP3→音符) ──▶ 预览可视化 ──▶ (可选)校正音符
──▶ 选风格/主题/配色/画幅 ──▶ 导出视频 ──▶ 下载/分享
每一步都要可"提前看到结果"(实时预览),降低不确定感。
3. 功能清单 (Features)
3.1 输入
- ✓ 上传 MP3 / WAV / m4a
- ✓ 上传 MIDI(跳过转写,质量最高)
- ○ 录音输入(移动端)
- ✓ 内置示例曲目(仅供体验试用,非内容曲库)
3.2 转写 (MP3 → 音符 JSON)
- ○ 自动 Audio-to-MIDI(见
05-tech-deep-dive.md) - ○ 可选源分离:抽取钢琴/人声/伴奏后再转写
- ○ 量化 (quantize) 到节拍,去抖动
- ○ 左右手 / 多音轨自动分配
3.3 编辑 / 校正(差异化体验)
- ○ 钢琴卷帘 (piano-roll) 编辑器:增删/移动音符、改时长
- ○ 调整 BPM / 偏移对齐
- ○ 标注左右手、分轨上色
3.4 可视化 / 风格(原型已实现 12 种效果)
🎹 钢琴特效(产品主打,面向 YouTube 弹奏素材) —— js/piano-fx.js,
共用下落音符+88键底座 + 可插拔「击键特效」,加新效果只需加一个 preset:
- ✓ 经典发光 / ⛲ 粒子喷泉(Rousseau) / 🔆 光束Saber / 💧 涟漪冲击波
- ✓ 🔥 火焰拖尾 / 🎆 烟花迸发 / 🌈 12色喷泉(按音高着色) / 🪞 镜面倒影
- ✓ 左右手分色 或 12 音高分色;多主题(极光/霓虹/日落/极简)
🎵 音频特效(通用背景) —— js/effects.js,实时 AnalyserNode 驱动:
- ✓ 📊 频谱柱 / 🌀 环形频谱 / 〰️ 波形带 / ✨ 粒子流光
其它(待做):
- ○ 画幅切换:16:9 / 9:16 / 1:1
- ○ 背景图/视频、Logo 水印、标题字幕
- ○ 更多钢琴特效:3D 视角、音符拖尾轨迹、键盘霓虹边框、烟雾、bokeh 背景
- ○ glitch/VHS 滤镜、歌词同步
音频特效由实时音频分析驱动,换真实 MP3 即可复用;钢琴特效由音符 JSON 精确驱动击键时机。
3.5 导出
- ○ 720p(Free)/ 1080p / 4K
- ○ 客户端录制导出(MediaRecorder / WebCodecs)
- ○ 服务器端离线渲染(高质量/批量/4K)
- ○ 导出帧序列 / 透明通道(高级)
3.6 账户 / 商业
- ○ 登录、订阅、点数、用量计费
- ○ 水印控制(按档位)
- ○ 项目保存 / 历史 / 云端
3.7 B2B
- ○ REST API:
POST /transcribe, POST /render - ○ Webhook 回调、用量统计、Key 管理
4. 非功能需求
- 同步精度:音画延迟 < ~30ms(人耳/眼可感知阈值附近)。
- 预览性能:60fps 实时渲染,88 键 + 数百音符不掉帧。
- 转写时延:3–4 分钟歌曲 < ~1 分钟出转写结果(目标)。
- 跨端一致:Web 预览与最终导出视觉一致(同一渲染内核)。
- 可访问性:键盘可视化对色弱友好(可选高对比主题)。
5. Web vs App 分工
| 能力 | Web | App |
|---|
| 创作/编辑/精修 | 主力(大屏、卷帘编辑) | 简化版 |
| 录音输入 | 次要 | 主力 |
| 快速出片/分享 | ✅ | 主力(社交分享场景) |
| 4K/批量 | ✅ | 触发服务器渲染,App 等通知 |
共享同一套「音符 JSON + 渲染内核」,Web/App/服务器渲染三端复用,保证一致性。这是架构的关键决策(见 03-technical-architecture.md)。
03 · 技术架构 (Technical Architecture)
本篇讲架构原理与目标态。第一版的具体部署选型、托管服务、计费机制、月成本见 06-mvp-v1.md §3–§4。
1. 设计原则
- 单一中间表示 (Single Source of Truth):一切围绕 音符 JSON(notes JSON)。
MP3 → 音符JSON → 像素。转写产出它,编辑修改它,三端渲染消费它。
- 同一渲染内核,多处复用:Web 预览、App、服务器导出共用同一份渲染逻辑(TypeScript),避免"预览和成品不一致"。
- 成本可控的渲染分层:免费/轻量用客户端渲染(成本≈0);高质量/批量/4K 用服务器渲染。
- 可演进的转写:转写模型可替换(Basic Pitch → 更强模型/自研),接口不变。
2. 中间表示:音符 JSON
{
"title": "Song name",
"bpm": 120,
"duration": 184.0, // 秒
"notes": [
{ "midi": 60, "start": 0.0, "dur": 0.5, "vel": 0.82, "hand": "R" }
// midi: 21..108 ; start/dur: 秒 ; vel: 0..1 ; hand: "R"|"L"
]
}
原型里 prototype/web/js/songs.js 和后端
prototype/backend/transcribe.py 都用这个格式。
3. 系统全景
┌──────────────────────────────────────────┐
用户 (Web / App) │ 前端 │
─────────────▶ │ 上传 UI · 实时预览(Canvas/WebGL渲染内核) │
│ piano-roll 编辑器 · 客户端导出(MediaRec.) │
└───────────────┬──────────────────────────┘
│ HTTPS / REST
┌───────────────▼──────────────────────────┐
│ API Gateway │
│ Auth · 计费/用量 · 限流 · 任务派发 │
└───────┬───────────────────────┬──────────┘
│ │
┌──────────────▼───────┐ ┌──────────▼───────────┐
│ 转写服务 (Worker) │ │ 渲染服务 (Worker) │
│ 源分离(可选 Demucs) │ │ headless 渲染内核 │
│ Audio→MIDI(Basic │ │ + ffmpeg → MP4 (4K) │
│ Pitch等) → 音符JSON │ │ (GPU 可选) │
└──────────┬───────────┘ └──────────┬───────────┘
│ │
┌──────────▼────────────────────────────▼──────────┐
│ 队列(SQS/Redis) · 对象存储(S3) · CDN · DB │
└───────────────────────────────────────────────────┘
4. 组件与技术栈(建议)
| 层 | 选型建议 | 理由 |
|---|
| Web 前端 | TypeScript + React + Vite;渲染内核用 Canvas2D(MVP)→ WebGL/PixiJS(性能) | 生态成熟,渲染内核可独立成包跨端复用 |
| 渲染内核 | 独立 npm 包 @pv/renderer,纯函数 render(notes, theme, t) → frame | Web/App/服务器共用,保证一致 |
| 移动 App | React Native / Expo 或 Flutter;复用渲染内核(RN 可走 WebView/Skia) | 跨平台、复用度高 |
| API 网关 | FastAPI (Python) 或 Node/NestJS | 转写在 Python 生态,FastAPI 顺手 |
| 转写 Worker | Python:Basic Pitch / (可选 Demucs 源分离) | 见技术深挖文档 |
| 渲染 Worker | Node + headless Chromium (Puppeteer/Playwright) 或 Remotion,配 ffmpeg;或纯 ffmpeg+canvas | 复用同一 JS 渲染内核做离线出片 |
| 队列 | Redis / SQS | 异步长任务 |
| 存储/CDN | S3 + CloudFront(或对应云) | 成品分发 |
| DB | Postgres | 用户/项目/用量 |
| Auth/计费 | Clerk/Auth0 + Stripe | 快速上线 |
5. 两种渲染路径(重要权衡)
路径 A — 客户端实时渲染 + 录制(MVP 首选)
- 浏览器用渲染内核实时画,
MediaRecorder / WebCodecs 录成视频。 - ✅ 服务器成本≈0,✅ 即时预览即成品;
- ⚠️ 受用户机器性能影响,4K/长视频/一致性较弱,录制格式受浏览器限制。
路径 B — 服务器端离线渲染(高级档)
- Worker 用同一渲染内核逐帧画(headless 或 Remotion)→ ffmpeg 合成 MP4 + 原音频。
- ✅ 4K、一致、可批量、可加复杂特效;⚠️ 计算贵 → 作为付费墙。
决策:MVP 用 A 跑通闭环,把 B 留给 Pro/4K/批量。两者共用渲染内核。
6. 数据流(一次出片)
- 前端上传 MP3 → API 存到 S3,建任务入队。
- 转写 Worker:(可选源分离) → Audio→MIDI → 量化/分手 → 写入音符 JSON到 DB/S3。
- 前端拉取音符 JSON → 实时预览 + 用户可校正/选风格。
- 导出:
- A:前端录制下载;
- B:提交渲染任务 → 渲染 Worker 出 MP4 → S3/CDN → 通知用户下载。
7. 扩展性 / 后续
- 转写模型热替换(同一接口);为不同曲风训练/微调。
- 渲染内核插件化主题(社区/市场)。
- 对重复上传做转写结果缓存/去重(同一文件指纹命中即复用,省算力)。
- 多人协作 / 项目版本。
8. 本仓库原型与目标架构的对应
04 · MVP 范围与路线图
本篇是分阶段路线图(全景视角)。第一版的具体落地定稿(含怎么收费、怎么部署上线)见 06-mvp-v1.md。
1. MVP 的一句话目标
用户上传一个 MP3,几分钟内得到一个可下载的「钢琴下落音符 + 键盘发光」视频。
闭环 = 上传 → 转写 → 预览 → 导出。其它全部砍掉。
2. MVP 范围 (Scope)
In(必须有)
- 上传 MP3 / 也支持 MIDI(MIDI 质量更稳,作为兜底卖点)
- 自动转写 MP3 → 音符 JSON(Basic Pitch;先聚焦钢琴/简单编制)
- 实时预览:下落音符 + 88 键发光(原型已完成核心)
- 至少 3 套主题、左右手分色、横屏导出
- 客户端录制导出 1080p(带水印)
- 简单账户 + 一个付费档(去水印/1080p)
Out(MVP 不做,留给后续)
- 4K / 服务器渲染 / 批量
- piano-roll 精修编辑器(先给"重转写/调BPM/调偏移"几个旋钮)
- 移动 App(先做 Web 响应式)
- 源分离、复杂复调高精度转写
- B2B API、协作(不做曲库:我们只提供服务,用户自带音源)
3. 阶段与里程碑
| 阶段 | 目标 | 交付物 | 粗估 |
|---|
| P0 原型(本仓库)✅ | 验证视觉与同步体验 | 可运行 web 原型 + 文档 | 已完成 |
| P1 转写打通 | MP3→音符JSON 自动化 | 后端转写服务 + 前端接入真实文件 | 1–2 周 |
| P2 闭环 MVP | 上传→预览→导出可下载 | 客户端录制导出 + 主题/画幅 + 水印 | 2–4 周 |
| P3 商业化 | 账户/订阅/付费墙 | 登录 + Stripe + 用量 | 2–3 周 |
| P4 质量与规模 | 4K/服务器渲染/编辑器/App | 渲染 Worker、piano-roll、RN App | 持续 |
时间为单人/小团队粗估,仅供排序参考。
4. 成功指标
- 激活:注册后成功导出第一个视频的比例 > 40%。
- 北极星:每周成功导出视频数。
- 质量:转写"可用率"(用户不需要大改即可出片)> 60%(钢琴类)。
- 转化:免费→付费 > 3%(去水印/画质驱动)。
5. 立即可做的下一步(从原型到 P1)
- 跑通
prototype/backend:本地 POST 一个 MP3 → 拿到音符 JSON。 - 前端加"上传 MP3"按钮,调后端拿 JSON,喂给现有渲染内核。
- 用真实音频元素(
<audio> 播 MP3)替换合成器,做音画同步对齐(offset 微调旋钮)。 - 加
MediaRecorder 录制导出按钮,先导 webm/mp4。 - 用自己创作/已授权的曲子做 3 条 demo,铺第一批种子内容(产品本身不含曲库)。
6. 风险驱动的验证顺序
先验证最不确定的:转写质量到底够不够用。
→ P1 先拿 10–20 首不同曲风的真实歌曲跑 Basic Pitch,人工评估"可用率",据此决定:
- 够用 → 直接做闭环;
- 不够 → 加源分离 / 限定曲风 / 强化校正编辑器 / 换模型。
05 · 技术深挖:MP3 → 音符(最难的环节)
整个产品成败的核心在于:把任意音频转成"哪个键、什么时候、按多久"的演奏信息(Audio-to-MIDI / 自动音乐转写 AMT)。其余(渲染、UI)相对成熟。
1. 问题难度分级
| 输入 | 难度 | 说明 |
|---|
| 用户直接给 MIDI | ⭐ 容易 | 直接解析,质量最高 → MVP 必须支持,作为兜底卖点 |
| 独奏钢琴录音 | ⭐⭐ 中 | 现有模型效果不错(Onsets&Frames、Basic Pitch) |
| 单旋律乐器/人声哼唱 | ⭐⭐ 中 | 单音高跟踪较成熟 |
| 完整流行乐(鼓+贝斯+人声+和声) | ⭐⭐⭐⭐ 难 | 复调 + 打击乐干扰,直接转易出错音 → 需源分离 |
策略:MVP 聚焦 ⭐~⭐⭐(钢琴/简单编制/MIDI 直传),复杂流行乐放到后续 + 源分离。
2. 可选技术方案
2.1 Audio→MIDI 转写模型
| 方案 | 类型 | 优点 | 缺点 | 适合 |
|---|
| Spotify Basic Pitch | 开源、轻量 | 易用、可本地跑、复调可用、支持多乐器 | 复杂混音精度有限 | MVP 首选 |
| Google Onsets & Frames | 开源 | 钢琴转写经典、质量高 | 偏钢琴、较重 | 钢琴专精 |
| Google MT3 | 开源、Transformer | 多乐器、强 | 重、部署成本高 | 进阶 |
| 商业 API(各家) | 闭源 | 省事 | 成本、依赖第三方 | 快速验证 |
| 自研/微调 | — | 护城河 | 投入大 | 规模化后 |
本仓库后端用 Basic Pitch 作为默认实现。
2.2 源分离(可选前置)
完整歌曲先分离出"钢琴/某乐器/人声"再转写,能大幅减少错音:
- Demucs(Meta,开源,质量好)
- Spleeter(Deezer,较老但快)
流程:MP3 → Demucs 分离 → 取目标音轨 → Basic Pitch 转写 → 音符 JSON。
2.3 后处理(很重要,直接决定观感)
转写原始输出往往"毛刺多"。后处理显著提升可用性:
- 量化 (quantize):把音符起止吸附到节拍网格(需先估 BPM/beat tracking,如 librosa)。
- 去碎音:合并过短/抖动音符,删极弱噪声音符。
- 音域裁剪:限制到 88 键。
- 左右手 / 分轨:按音高阈值或聚类分 hand=R/L 上色。
- 力度归一:velocity 映射到 0..1 给视觉发光强度。
3. 质量不够怎么办(UX 兜底)
转写永远不可能 100%。把"修正"做成顺滑体验是差异化:
- 重转写参数:灵敏度、最短音符、是否源分离 —— 给用户几个旋钮一键重算。
- piano-roll 编辑器:手动增删/移动音符(后续)。
- BPM / 偏移对齐:让下落音符与听感对齐。
- MIDI 直传通道:高级用户/音乐人直接给 MIDI,绕过转写。
4. 推荐管线(目标态)
MP3
│
├─(可选) Demucs 源分离 ─▶ 目标音轨(钢琴/人声)
│
▼
Basic Pitch 转写 ─▶ 原始音符 + onset/力度
│
▼
后处理:beat tracking(librosa) → 量化 → 去碎音 → 分左右手 → 力度归一
│
▼
音符 JSON ──▶ 渲染内核
5. 本仓库实现
评估建议:先拿 10–20 首代表性曲目跑一遍,人工标"可用率",再决定是否引入源分离/换模型(见 04-mvp-roadmap.md §6)。
06 · 第一版 MVP(最终落地闭环 V1)
04 是"路线图/分阶段",本篇是把第一版方向拍死:第一版到底做成什么样、怎么收费、怎么部署上线。
一句话目标:一个网站,上传音乐 → 出一个带特效的钢琴视频,能注册、能付费去水印、能跑在真实服务器上。
1. 第一版方向(已确定)
做减法,先把闭环跑通、能收到第一笔钱。
| 维度 | V1 决定 | 理由 |
|---|
| 端 | 仅 Web(响应式,手机能用) | App 留 V2,先验证需求 |
| 输入 | MP3/WAV/M4A 上传 + MIDI 直传 + 内置示例(仅体验) | 用户自带音源;不做曲库 |
| 转写 | Basic Pitch,主打独奏钢琴/简单编制 | 复杂流行乐放 V2(加源分离) |
| 可视化 | 现有 12 种特效 + 主题 + 画幅 + 标题 + 水印 | 原型已完成 |
| 导出 | 客户端录制(MediaRecorder)→ webm/mp4 | 服务器渲染成本高,V1 不做(省钱) |
| 账户 | 邮箱 + Google 登录 | 注册门槛低 |
| 收费 | 订阅(Free/Creator/Pro) + 转写额度 + 水印闸 | 见 §3 |
| 市场 | 全球优先(YouTube/Shorts/TikTok) | 支付与合规简单;国内(抖音/B站)做 V2 本地化 |
V1 明确不做:4K/服务器渲染、批量导出、移动 App、piano-roll 精修编辑器、源分离、B2B API、国内支付、曲库/内容分发(永不做——纯工具,用户自带音源)。
关键省钱逻辑:导出走客户端(用户自己机器渲染),服务器只承担"转写"这一点计算 → V1 几乎没有渲染成本。付费点是"去水印 + 转写额度 + 画质"。
2. V1 产品具体描述
2.1 完整用户流程(已在原型实现外壳)
落地页 → 注册/登录 → 上传音乐 → 处理(真实转写) → 工作室(选特效/主题/画幅/标题)
→ 导出视频(免费带水印 / 付费去水印) → 下载 → 发到 YouTube/TikTok
2.2 功能边界(V1)
- 上传:拖拽 MP3/WAV/M4A(≤ ~10 分钟、≤ ~30MB);或 MIDI;或选内置示例曲目(仅供体验,非内容曲库;音源由用户自带)。
- 转写:后端 Basic Pitch → 音符 JSON(含量化、分左右手、力度归一)。失败/质量差时给"重试/换灵敏度/改用 MIDI"提示。
- 工作室:12 特效实时预览、4 主题、3 画幅(16:9/9:16/1:1)、标题叠加、水印开关(受套餐控制)。
- 导出:客户端录制,免费 1080p+水印;付费 1080p 去水印(4K 留 V2)。
- 账户:登录、套餐状态、转写额度、历史项目列表(项目 = 保存的音符 JSON + 设置)。
2.3 北极星 & V1 成功标准
- 北极星:每周成功导出视频数。
- V1 上线判定:"上传真实 MP3 → 下载到成片"全链路可用,且能完成一笔真实付费。
- 目标:注册→首次导出 > 40%;免费→付费 > 3%;钢琴类转写"可用率" > 60%。
3. 怎么收费(计费方案)
3.1 套餐设计(V1)
| 套餐 | 价格 | 内容 | 付费点 |
|---|
| Free | ¥0 | 1080p、带水印、每月 3 次转写、全部特效/主题 | 体验 + 传播(水印) |
| Creator | ~$9/月 (或 ¥39) | 去水印、每月 50 次转写、竖屏/方形、更长时长 | 主力收入 |
| Pro | ~$19/月 | 去水印、无限转写(合理用量)、优先转写队列、自定义标题样式 | 重度用户 |
| Credit 包 | 按量 | 不想订阅的人,买"转写次数/导出次数"点数 | 长尾 |
V1 把"全部特效"都给免费用户(特效是传播点,不该锁)。付费墙 = 去水印 + 转写额度 + 时长。
3.2 用什么支付服务
| 方案 | 适用 | 优劣 |
|---|
| Paddle / Lemon Squeezy(推荐 V1) | 全球面向个人/独立开发 | ✅ Merchant of Record:帮你处理全球增值税/销售税、发票、退款,省去合规噩梦;✅ 接入快 | 抽成略高(~5%+) |
| Stripe | 主要美区/自建合规 | ✅ 费率低、生态强 | ❌ 需自己处理各国税务 |
| 微信支付/支付宝 | 国内市场 | 国内必需,但需公司主体+合规 → 放 V2 |
V1 决定:用 Lemon Squeezy / Paddle(MoR),最快合规收全球的钱;国内支付等 V2 做本地化版本。
3.3 计费怎么落地(技术机制)
计费的"闸"放在两个最有价值的动作上:转写(有成本)和去水印导出(付费特权)。
用户登录 → 拿到 JWT
↓
点"开始转写" → 前端带 JWT 调后端 /transcribe
↓
后端先查 DB:该用户套餐 & 本月已用转写次数
├─ 超额/无权限 → 返回 402,前端弹"升级套餐"
└─ 允许 → 跑 Basic Pitch,转写次数 +1,返回音符 JSON + { watermark: 是否强制 }
↓
导出时:watermark 标志由后端套餐决定(前端不可绕过的关键校验放后端签发的 token 里)
↓
支付:Lemon Squeezy/Paddle 结账 → Webhook 回调后端 → 更新 DB 里用户的套餐/额度
- 额度计量:DB 里每个用户一行
plan、transcribe_used、period_start;每月重置。 - 防绕过:水印逻辑虽然在前端渲染,但"是否有权去水印"由后端在转写响应/导出授权里下发;免费用户前端强制打水印。真正值钱的成本动作(转写)在后端卡死。
- Webhook:订阅创建/续费/取消/退款 → 改
plan。
4. 怎么发布上线(部署方案)
4.1 V1 架构(精简版,按"少运维 + 低成本"选型)
┌─────────────────────────────┐
浏览器 ──────▶ │ 前端 (静态站) │ Vercel / Cloudflare Pages
│ 工作室 + 客户端导出(MediaRec) │
└───────────┬─────────────────┘
│ HTTPS (JWT)
┌───────────▼─────────────────┐
│ API (FastAPI 容器) │ Render / Railway / Fly.io
│ 鉴权·额度·计费回调·调度转写 │
└─────┬───────────────┬───────┘
│ │ 调用
┌──────────▼────┐ ┌──────▼─────────────┐
│ Supabase │ │ 转写 (Basic Pitch) │ Replicate / Modal
│ Auth+Postgres │ │ 按次计费、无闲置成本 │ (或同容器 CPU 跑)
│ +Storage(音频) │ └────────────────────┘
└────────────────┘
▲
┌──────────┴────┐
│ Lemon Squeezy │ 支付 + Webhook
└───────────────┘
4.2 具体选型与理由
| 组件 | 选型 | 理由 |
|---|
| 前端托管 | Vercel 或 Cloudflare Pages | 静态站,免费档够用,全球 CDN,自动 HTTPS |
| 后端 API | Render 或 Railway(容器) | 部署一个 Docker 就行,便宜(~$7–25/mo),自带 HTTPS |
| 转写计算 | Replicate / Modal(按调用付费) | 无请求时零成本,避免养闲置 CPU;Basic Pitch 可打包成一个推理端点。流量大了再自建 |
| 认证 | Supabase Auth(邮箱+Google) | 与 DB 一体,省事 |
| 数据库 | Supabase Postgres | 用户/套餐/额度/项目;免费档起步 |
| 对象存储 | Supabase Storage 或 Cloudflare R2 | 暂存上传音频;R2 出口免费、便宜 |
| 支付 | Lemon Squeezy / Paddle | 见 §3.2,MoR 省税务 |
| 域名/CDN/DNS | Cloudflare | 域名 + DNS + 防护 |
| 监控 | Sentry(前后端) + 平台自带日志 | 错误可见 |
选型原则:能托管就不自运维,能按量就不养机器。 单人/小团队 V1 几乎不碰服务器运维。
4.3 V1 月成本估算(低流量起步)
| 项 | 估算 |
|---|
| Vercel/Cloudflare Pages | $0(Hobby/Free) |
| Render/Railway API | $7–25 |
| Supabase | $0 → $25(Pro) |
| 转写 (Replicate 按次) | ~$0.01–0.05/首 × 用量 |
| 域名 | ~$12/年 |
| 合计 | 约 $30–60/月 + 转写按量,跑起来后用收入覆盖 |
4.4 上线检查清单
- ○ 域名 + HTTPS + 自定义邮箱
- ○ 注册/登录/找回密码可用
- ○ 上传 → 真实转写 → 工作室 → 导出 全链路通过(含失败兜底)
- ○ Free/Creator 套餐 + Lemon Squeezy 结账 + Webhook 改额度 跑通一笔真实付款
- ○ 转写额度计量 & 超额 402 提示
- ○ 免费版强制水印、付费去水印 验证
- ○ ToS / 隐私政策 / 版权声明(版权是最大风险,见 01 §6)
- ○ Sentry 报错监控接好
- ○ 落地页 SEO(title/描述含 "piano visualizer / 钢琴可视化")+ 3 条种子示例视频
5. V1 落地计划(4–6 周)
| 周 | 目标 |
|---|
| W1 | 接转写后端:上传 MP3 → 调 Basic Pitch → 音符 JSON 喂现有工作室;真实音频播放(替换合成器) |
| W2 | Supabase 登录 + 项目保存 + 转写额度计量 |
| W3 | Lemon Squeezy 套餐/结账/Webhook + 水印闸 |
| W4 | 部署(Vercel+Render+Supabase) + 域名 + 监控 + 兜底/错误处理 |
| W5 | ToS/版权/隐私、落地页打磨、SEO、少量体验示例曲目 |
| W6 | 内测 → 修 → 公开发布,铺种子内容 |
风险驱动:W1 先验证转写"可用率"够不够(拿 10–20 首真实曲子人工评估),不够就限定曲风/强化"换灵敏度/改 MIDI"兜底,再继续。
6. 与其它文档的关系
07 · 本地开发环境(如何在本地跑完整流程)
目标:在你自己电脑上,从 0 到"上传音乐 → 真实识别音符 → 工作室出片"全程跑通。
1. 依赖
- Python 3.9+(转写后端 / basic-pitch)
- Node.js(仅用于
tools/build-docs.js 生成文档站,可选) - 浏览器(Chrome/Edge/Safari)
- 可选:Docker(一键起整套,见
08-deployment.md)
2. 最快路径(用 Makefile)
# A. 免 ML 联调(秒级,先确认链路通)
make setup-mock # 只装 fastapi/uvicorn
make dev-mock # 同时起 后端(模拟) + 前端
# 浏览器打开 http://localhost:9000 → 上传任意音频 → 工作室 → 导出
# B. 真实转写(装 basic-pitch,首次较慢)
make setup # 装 basic-pitch 等全部依赖
make dev # 同时起 后端(真实) + 前端
make help 查看所有命令。
macOS:make setup 会带 coremltools,basic-pitch 自动用 CoreML,无需额外配置(已实测可用)。
3. 手动路径(不想用 make)
两个终端:
# 终端 1 — 后端
cd prototype/backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # 真实;或仅 fastapi uvicorn[standard] python-multipart 走模拟
uvicorn app:app --reload --port 9001 # 真实;模拟加前缀 PR_MOCK=1
# 终端 2 — 前端
cd prototype/web && python3 -m http.server 9000
浏览器 → http://localhost:9000。
前端后端地址在 prototype/web/js/config.js 的 BACKEND_URL(默认 http://127.0.0.1:9001)。
4. 完整数据流(本地)
浏览器上传音频
→ POST http://localhost:9001/transcribe (FastAPI, app.py)
→ transcribe.py 调 basic-pitch 出音符
→ 返回音符 JSON
→ 前端 loadSong() 喂给工作室渲染内核
→ 播放用户真实音频(<audio>) + 可视化
→ 导出 (MediaRecorder 录画面+声音) → 下载 .webm
后端不可达时 → 前端自动回退示例曲目(仍可玩)
5. 命令行单独转写(不开前端)
cd prototype/backend
python transcribe.py 某首歌.mp3 > song.json # 出音符 JSON
PR_MOCK=1 python -c "import transcribe,json;print(json.dumps(transcribe.mock('x')))" # 模拟
6. 评估转写质量(关键验证)
make gen-audio # 生成带真值的合成测试音频到 tests/audio/
make eval # 跑转写 + 算 F1
把你自己的真歌 .mp3 放进 tests/audio/ 再 make eval:
- 有同名
.mid / .truth.json → 输出 Precision/Recall/F1 - 没真值 → 只产出
<name>.notes.json 供前端肉眼评估
合成测试基线(已实测):单音/左右手 F1=1.00,三音和弦 F1≈0.86。
说明 basic-pitch 对干净钢琴/简单编制很好;复杂流行乐建议先做源分离(见 05)。
7. 常见问题
- 上传 MP3 后只出示例曲目:后端没起或地址不对 → 检查 9001 端口与
config.js。 - basic-pitch 装不上:先用
make dev-mock 验证链路;真实转写的运行时见 requirements.txt 注释(Mac=CoreML / Linux=onnxruntime)。 - 导出没有声音:导出是实时录制,需保持标签页在前台播放完整首曲子。
- 预览里动画静止:离屏环境会节流动画;真实浏览器标签页正常。
08 · 服务器搭建方案(部署上线)
06 讲了选型策略与成本,本篇是可直接照做的部署步骤 + 仓库里已备好的部署文件。
原则:能托管就不自运维,能按量就不养机器。
1. 部署拓扑(V1)
用户浏览器
│
├──────────────▶ 前端静态站 (Vercel / Cloudflare Pages) ← prototype/web
│
└── /transcribe ▶ 转写 API (Render / Railway, Docker) ← prototype/backend
│
(V2 可拆) 转写计算 → Replicate/Modal 按量
支付:Lemon Squeezy/Paddle Webhook → API (账户/计费见 06 §3)
V1 把转写跑在 API 容器里即可;流量大了再把转写拆到 Replicate/Modal。
2. 仓库里已备好的部署文件
| 文件 | 作用 |
|---|
prototype/backend/Dockerfile | 后端容器(Linux,装 onnxruntime 跑 basic-pitch) |
prototype/backend/.dockerignore | 镜像瘦身 |
prototype/backend/.env.example | 环境变量模板(PR_MOCK / ALLOWED_ORIGINS) |
docker-compose.yml | 本地一键起整套(后端+前端) |
render.yaml | Render 一键部署蓝图 |
3. 后端上线(Render,最省事)
- 仓库推到 GitHub。
- Render → New → Blueprint → 选本仓库(自动读
render.yaml)。 - 部署后在服务的 Environment 填:
ALLOWED_ORIGINS = https://你的前端域名(收紧 CORS)
- 健康检查
/health 通过即上线。API 地址形如 https://pianoreel-api.onrender.com。
Railway / Fly.io 同理:直接用 prototype/backend/Dockerfile 部署一个容器,暴露 9001,配 /health。
自己用 Docker 跑(任意云主机)
cd prototype/backend
docker build -t pianoreel-api .
docker run -p 9001:9001 -e ALLOWED_ORIGINS=https://你的域名 pianoreel-api
4. 前端上线(Vercel / Cloudflare Pages)
前端是纯静态目录 prototype/web,无需构建。
- Vercel → New Project → 选仓库 → Root Directory 设为
prototype/web → 部署。 - 部署后改
prototype/web/js/config.js 的 BACKEND_URL 为线上 API 地址,重新部署。
(生产建议改成读环境/构建注入,V1 直接改常量即可。)
- 绑定自定义域名(用 Cloudflare 管 DNS)。
也可用 cloudflare pages / netlify,同样指向 prototype/web 目录。
5. 本地用 Docker 验证整套
docker compose up --build
# 前端 http://localhost:9000 API http://localhost:9001
# 想免 ML:把 docker-compose.yml 里 backend 的 PR_MOCK=1 取消注释
6. 上线前 checklist
- ○ 后端
/health 200;/transcribe 真实出音符 - ○
ALLOWED_ORIGINS 收紧到前端域名(不要留 *) - ○ 前端
BACKEND_URL 指向线上 API(https) - ○ 自定义域名 + HTTPS
- ○ 上传体积/时长限制(防滥用:当前 app.py 可加大小校验)
- ○ 错误监控(Sentry)
- ○ 账户/计费(见 06 §3:Supabase + Lemon Squeezy)—— V1 可分两步上线
- ○ ToS / 隐私 / 版权声明(我们不提供曲库、用户自带音源,责任在用户)
7. 成本与扩展
- 起步:前端 $0 + 后端 $7–25/mo + 域名。见
06-mvp-v1.md §4.3。 - 扩展:转写拆到 Replicate/Modal(按调用计费,零闲置);对重复上传按文件指纹缓存转写结果省算力。
- 注意:basic-pitch 在 Linux 用 onnxruntime(Dockerfile 已装),Mac 用 CoreML(本地开发自动)。
09 · AI 自动编曲(二次创作)
把"听一首歌 → 弹出来"做到音乐家水平:不只是扒出旋律,而是配上好和声、好织体,
生成一份真正能弹的钢琴谱(MIDI / 五线谱),并渲染成视频。
1. 目标:按"演奏水平"分级
| 水平 | 能力 | 技术实现 | 状态 |
|---|
| 🎓 学生 | 听出旋律、单手弹 | Demucs 人声 → melody.to_melody() 单音旋律 | ✅ 已有 |
| 👨🏫 音乐老师 | 配合理和弦、双手弹 | arrange.detect_chords() + render_accomp() 规则编配 | ✅ 已有(粗糙) |
| 🎹 音乐家 | 高级和声 / voicing / 装饰 / 强弱 | AI 编曲(Claude) | ⬜ 本文档 |
难度由用户选(新手 / 进阶 / 炫技)—— 对应"老师按学生水平教"。
2. 总流水线
MP3 → Demucs(4 stems) → 转写 → 旋律 + 检测和弦
→ ① 量化到节拍网格(小节 / 四分·八分音符)
→ ② Lead Sheet(旋律 + 和弦,紧凑文本 / ABC)
→ ③ AI 编曲(Claude,混合策略)
→ ④ 渲染:可弹 MIDI / MusicXML / PDF 五线谱 + 视频
3. 表示格式:为什么用 ABC
不能直接把音符 JSON 丢给 LLM(token 大、易错)。用 ABC 记谱法(文本乐谱):
- 极紧凑(一首歌几 K token),LLM 天生会读写
- 无损转 MIDI / MusicXML / 五线谱
- 和弦写在引号里:
"C"E2 G2 | "Am"A2 c2 |
两种表示分工:
- 演奏表示(notes / MIDI):给可视化、合成器、视频渲染用(时间 + 力度)
- 乐谱表示(ABC / MusicXML):给人看 / 打印,也是 LLM 推理音乐的最佳接口
4. AI 策略
策略 A — LLM 直接生成完整编曲(ABC)
喂"旋律 + 调性 + 风格" → LLM 输出双手 ABC → 解析成 MIDI。
简单灵活,但 LLM 偶有越界 / 节奏错,需校验 + 修复。
策略 B — 混合:LLM 决策 + 规则落音(推荐)
LLM 只输出高层计划(每段和弦走向 + 伴奏风格 + 强弱),不直接写音符;
arrange.py 规则引擎把计划渲染成实际音符 → 保证在音域、踩拍、能弹。
输出 token 少 → 更便宜,几乎不产出"不能弹"的谱。
5. 技术细节
① 量化(quantize)
- 输入:转写的毛糙时间(如 3.247s)+
bpm + beats[](节拍点) - 把每个音的起始 / 时长对齐到节拍网格(1/4、1/8、1/16 拍)
- 估计调性:音高类直方图与大/小调音阶模板相关(Krumhansl)
- 我们自动生成的编曲本就踩着拍子,比原始转写更易记谱 → 先编曲、后记谱
② Lead Sheet 生成
- 调性、拍号、速度(header)
- 旋律:量化后的音名 + 起始拍 + 时值拍
- 和弦:
detect_chords() 的进行(AI 可重配)
③ AI 编曲调用(Claude)
- 接 Anthropic SDK,开 prompt 缓存(系统提示词复用降本)
- 系统提示词见 §7
- 输出解析:ABC → notes;校验音域 [21,108]、消除重叠、补齐时长
- 难度参数控制密度 / voicing / 装饰
④ 渲染与导出
- 可弹 MIDI:
@tonejs/midi 客户端导出(已有 ⬇MIDI 按钮) - MusicXML / PDF:
music21(Python)→ LilyPond / MuseScore 渲染,或浏览器 VexFlow / OpenSheetMusicDisplay - 视频:现有 Canvas + MediaRecorder
6. 成本(一首 4 分钟歌,AI 仅调用一次)
约 2.5–3K token 输入 + 1–3K 输出。每首成本:
| 模型 | 策略 | 每首 | 质量 | 用途 |
|---|
| Claude Haiku | 混合 | ~$0.01 | 够用 | 走量 / 免费兜底 |
| Claude Haiku | 直出 ABC | ~$0.02 | 够用 | |
| Claude Sonnet | 混合 / ABC | ~$0.02–0.05 | 好,乐感明显 | 付费档主力 |
| Claude Opus | 直出 ABC | ~$0.11–0.27 | 最强 | 炫技 / 溢价档 |
估算基于 Haiku≈$1/$5、Sonnet≈$3/$15、Opus≈$15/$75(每百万 token in/out),
以官方实时价为准;开 prompt 缓存后输入成本更低。
业务账(付费 $9/月,编 50 首/月):
- Sonnet:50 × $0.04 ≈ $2/月成本,毛利极高
- 一键三难度(×3 调用):仍 < $6/月,可作卖点
分档: 免费=纯规则(0 成本);付费=Sonnet 混合 + 缓存;高级=Opus。
7. 编曲系统提示词(模板)
你是专业钢琴编曲师。我给你一段从歌曲中提取的旋律(含调性、速度、拍号)。
请编配一份【难度:新手/进阶/炫技】的钢琴演奏:
要求:
- 右手:给定旋律(可加得体的经过音 / 八度加厚)。
- 左手:为旋律配一个该调内好听的和弦进行,并用【分解和弦/柱式/华尔兹/抒情】
织体演奏。
- 双手保持在舒适音区(左手 C2–C4,右手 C4–C6),符合钢琴演奏习惯。
- 保持在给定调性内;和弦要贴合旋律。
旋律格式:音名@起始拍(时值拍),调性 X,速度 Y BPM,Z/4 拍。
旋律:<<melody>>
只输出两部分:(1) 按小节的和弦进行;(2) ABC 记谱(双手,V:1 右手 / V:2 左手)。
8. 落地顺序
- Phase 1 量化 + Lead Sheet(地基)
- Phase 2 接 Claude(混合策略),在 Let It Go 上验证质量
- Phase 3 可弹 MIDI / 五线谱输出
- Phase 4 接入上传流程 + 免费/付费分级
9. 风险与备注
- AI 转写 → 漂亮五线谱本身难(节奏量化、变音记号);纯旋律 + 我们生成的规整编曲记谱质量更好。
- 校验层是必须的:LLM 输出要过音域 / 重叠 / 调性检查再渲染。
- 版权:仍是纯工具,用户自带音频;编曲是用户音频的衍生,归用户。
10 · 竞品对标:我们要达到什么水平、跟谁比
理解差距才知道往哪走。"好听"由两件独立的事决定,解法完全不同:
| 维度 | 是什么 | 我们现状 | 解法 |
|---|
| ① 音色 | 钢琴声本身好不好听 | 合成器(3 振荡器),电子、廉价 | 换真钢琴采样(最易补,性价比最高) |
| ② 音乐性 | 音准 / 节奏 / 编曲好不好 | 全自动转写,偏糙 | pYIN 旋律 + AI/规则编曲(正在做) |
三类竞品
A 类 · 演奏极好听(视觉 + 听感的对标)
- Rousseau(YouTube)—— 钢琴可视化天花板:流动音符 + 玻璃倒影 + 顶级音色
- Patrik Pietschmann、SeeMusic、Sheet Music Boss —— 同类高质量
- ⚠️ 关键真相:它们是真人演奏的 MIDI + 录制级钢琴采样,不是自动扒谱。
好听 = 人工编曲/演奏 + 好音色,而非算法。
B 类 · 下落音符播放器(视觉对标)
- Synthesia(经典)、Piano From Above、PianoVision (VR)
- 特点:只播放现成 MIDI,不做音频转写。视觉是我们的直接参照。
C 类 · 自动「音频 → 谱子」(我们真正的竞争对手)
- Klangio(Melody Scanner / Piano2Notes)—— 最直接对手,MP3 → 钢琴谱
- AnthemScore、Samplab、Moises(含转 MIDI)、Lalal.ai(分离)
- 特点:自动转写质量普遍一般(和我们面对同样难题),但音色用采样,听着顺。
我们的定位与差异
A 类好听 = 人工 + 好音色;C 类自动 = 转写糙但音色好。
我们做最难的全自动 MP3→可弹钢琴,所以音乐性天然更糙;
但音色这块是自己拖后腿——换采样即可补上。
差异化机会:
- 全自动(vs A 类要人工)
- 一键出视频(vs C 类只给谱子)
- 拆轨混音 + 多声部「看/弹/唱」(独有)
- AI 编曲分难度(新手/进阶/炫技)
目标水平(分阶段)
- 音色 → 逼近 Synthesia / Rousseau 的钢琴采样听感(短期,换 soundfont)
- 视觉 → 已接近 Rousseau 风格(倒影/粒子/特效,已有)
- 音乐性 → 旋律准(pYIN ✅ 方向对)+ 编曲好(AI/规则,进行中),目标"听得出原曲、能弹"
- 谱子 → 可下载 MIDI(✅)/ MusicXML / PDF,对标 Klangio
衡量标准
- 旋律可辨识度(盲听能否认出原曲)
- 可弹性(音域、密度、双手合理)
- 听感(音色 + 编曲,对标 Synthesia 中端)
- 视觉(对标 Rousseau)
11 · Pop2Piano 引擎 · 服务架构与优化
实测验证:Pop2Piano(本地"音频→钢琴"专用模型)明显优于自研拼接管线,
确立为核心编曲引擎。本文覆盖:怎么搭服务器、成本、优化方向。
0. 关键发现:管线大幅简化
Pop2Piano 直接吃整首歌音频 → 输出钢琴翻弹 MIDI,不需要 Demucs / 转写 / 配和弦。
| 旧自研管线 | Pop2Piano 引擎 |
|---|
| 步骤 | Demucs→pYIN→和弦→AI编左手→渲染 | Pop2Piano→渲染 |
| 单曲耗时(本地) | ~3-5 分钟(Demucs 最慢) | ~10-60 秒 |
| AI API 费 | 有(MiniMax/Claude) | 0(本地模型) |
| 编曲质量 | 拼接感 | 学过真人翻弹,更自然 |
| 风格 | 自定义织体 | composer1~21(21 种) |
Demucs 仅在「叠加原唱」可选功能里才需要,核心编曲不依赖它。
1. 推荐服务架构(异步 ML 模式)
ML 推理慢(秒级),不能阻塞 HTTP 请求。标准做法是上传→入队→后台 GPU 推理→轮询/推送结果。
用户浏览器
│ 上传 MP3 + 选风格(composer)
▼
[前端] 静态站 (Vercel / Cloudflare Pages)
│ POST /jobs
▼
[API] FastAPI (Render / Railway / Fly) ← 便宜、CPU、无状态
│ - 鉴权 / 配额 / 计费
│ - 存音频到对象存储,创建任务,入队,返回 job_id
▼
[队列] Redis + RQ/Celery (或 SQS)
▼
[GPU Worker] Pop2Piano 推理 ← 唯一花钱的重活
│ ① (可选)Demucs 抽人声 ② Pop2Piano → MIDI ③ 后处理(量化/动态)
│ 写结果(MIDI/notes JSON) 到对象存储
▼
[结果] 前端轮询 /jobs/{id} 或 WebSocket 推送 → 进工作室渲染 → 导出视频
- 前端:静态托管,几乎免费
- API:FastAPI 容器,CPU 即可,便宜
- 存储:Cloudflare R2 / Supabase Storage(音频 + MIDI 产物)
- 鉴权/数据库/计费:Supabase + Lemon Squeezy(见 doc 06)
- 渲染:MVP 用现有客户端 MediaRecorder(成本≈0);高清/4K 再上服务端
2. GPU 推理怎么部署(最关键的决策)
别开 24 小时常驻 GPU(烧钱)。按音量选:
| 方案 | 说明 | 成本 | 适合 |
|---|
| Replicate / Modal(无服务器 GPU)⭐ | 部署一次,按秒计费、缩容到零,不用时 0 成本 | ~$0.01–0.05/首 | MVP / 个人开发者首选 |
| RunPod / Beam | 类似,便宜 | 类似 | 量上来后 |
| 自租 GPU 机器 | 固定月费,空闲也烧 | $几十~上百/月 | 稳定大流量 |
| 纯 CPU(便宜 VPS) | Pop2Piano CPU 也能跑(慢),无需 GPU | VPS 月费 | 低量 / 极致省钱 MVP |
推荐:Pop2Piano 打包成 Replicate(cog) 或 Modal 函数,无服务器 GPU,按调用付费。 部署一次,自动扩缩,不用管运维。
3. 成本模型(单曲)
| 项 | 成本 |
|---|
| Pop2Piano 推理(无服务器 GPU) | ~$0.02–0.05 |
| AI API | $0(本地模型) |
| 存储/带宽 | 可忽略 |
| 客户端渲染 | $0 |
| 单曲合计 | ~$0.02–0.05 |
毛利极高。免费档=水印+低配,付费档=高清+多风格。
4. 优化方向
质量
- 挑最佳 composer:21 个跑一遍 A/B,定默认 + 映射到风格选择
- 后处理 MIDI:轻量化量化、套用我们的动态力度 + 混响
- 保旋律清晰:必要时用我们的 pYIN 旋律校正 Pop2Piano 右手(混合)
- 高质量音源:导出用 FluidSynth + 好的钢琴 SF2 / Pianoteq,远胜浏览器采样
- 难度分级:简化 MIDI(去装饰/减密度) 出"新手版"
性能
- 去掉 Demucs(Pop2Piano 不需要)→ 单曲快几倍
- GPU 推理 → 秒级
- 结果缓存:同曲同 composer 复用
- 后台队列:用户不卡等待,进度条/推送
- 长曲分块并行
成本
- 本地模型 = 无 per-call AI 费(vs MiniMax/Claude)
- 无服务器 GPU 缩容到零 = 无闲置成本
- 免费档 CPU 兜底 / 付费档 GPU 提速
产品
- 一次上传 → 多风格视频(跑几个 composer)
- 可下载:MIDI(已有)/ MusicXML / PDF 五线谱
- 叠加原唱/节拍(可选 Demucs)—— "钢琴 Live + 原唱"
- 调速/调性
5. 落地顺序
- MVP:FastAPI(Render) + Pop2Piano on Replicate/Modal + 前端(Vercel) + Supabase/Lemon Squeezy;异步任务 + 客户端渲染
- 提质:挑 composer、后处理、FluidSynth 高质量导出
- 产品化:风格=composer 映射、多版本、五线谱、原唱叠加
- 扩展:缓存、队列扩缩、服务端 4K 渲染
6. 结论
Pop2Piano 把"编曲"这个最难的环节用一个本地免费模型解决了,架构更简单、成本更低、质量更好。
PianoReel 定位收敛为:"上传任意 MP3 → AI 一键生成好看好听的钢琴 Live 视频(多风格)",
赢在编曲(Pop2Piano) + 音色 + 可视化 + 一键导出,而非转写准确度。
12 · 路线图:MVP 之后做什么
现状:本地 MVP 跑通,效果不错 —— 上传 MP3 → Pop2Piano 编曲(8s/20s)→
工作室(真钢琴+混响+动态+特效)→ 抽原声叠加 → 导出(视频/MIDI/高音质音频)。
定位收敛:「上传任意歌 → AI 一键生成钢琴 Live 视频(多风格)」。
核心原则:先把成品做完整 → 验证需求 → 再做成服务。 不要在验证前先建账户/付费/部署基建。
Phase 1 · 成品打磨(让输出能直接发布)← 现在做
目标:能产出一个完整、能发到 YouTube/B站 的 Piano Live 视频。
- ○ 整首歌处理 —— 去掉 60s 限制,支持完整 3-5 分钟(Pop2Piano 分块推理,慢但可行)
- ○ 导出优化 —— 导出的视频音质用 FluidSynth/高音质音源(现在视频走浏览器采样)
- ○ 速度 / 移调 / 换一版 —— 渲染层实时参数(B 类,不重跑模型)
- ○ MIDI 轻量后处理 —— 去极短碎音、轻量化,更干净
- ○ 画幅/片头片尾 —— 竖屏成品(Shorts/抖音)、标题卡
产出:1-2 个完整成品视频。
Phase 2 · 验证需求(最便宜的 go / no-go)
目标:花最小代价确认"有人要"。你有音乐频道,这是独特优势。
- ○ 用现有管线做 2-3 个完整 Piano Live 视频(挑热门歌)
- ○ 发到你的频道 / 社群 / 小红书 / YouTube Shorts
- ○ 看数据:播放、互动、"求教程/求工具"的评论
- ○ 决策:反响好 → 进 Phase 3 做成产品;反响平 → 调整定位/风格再试
Phase 3 · 做成服务(验证通过后才做)
目标:别人能上网用、能付费。架构见 doc 11。
- ○ 异步任务 + 进度条 —— Demucs/Pop2Piano 慢,上传→入队→后台→轮询
- ○ 部署:前端 Vercel/Cloudflare Pages + API FastAPI(Render) + Pop2Piano on Replicate/Modal(无服务器 GPU,缩容到零)
- ○ 账户:Supabase(Auth + DB + Storage)
- ○ 付费:Lemon Squeezy(MoR);免费档=水印+限量+短,付费档=高清+整首+多风格
- ○ 配额计量 —— 服务端限制次数
Phase 4 · 增长功能(产品成立后)
- ○ 一次上传 → 多风格视频(4 个 composer 各出一版)
- ○ 五线谱下载(MusicXML/PDF,music21)
- ○ 作品库 / 历史
- ○ 难度分级(新手/进阶/炫技)
- ○ 叠原唱成品("钢琴 Live + 原唱"做卖点)
法务 / 风险
- 版权:纯工具,用户自带音频;编曲是用户音频的衍生,归用户。ToS 写清。
- 不建曲库、不分发音乐(规避版权)。
建议的立刻动作
先做 Phase 1 的「整首歌处理」 —— 这是产出第一个完整成品的前提,做完就能进 Phase 2 用你的频道验证。
13 · YouTube 链接一键导入(粘贴链接 → 自动下载 → 直接出片)
目标:用户在上传页粘贴一个 YouTube 链接,系统自动下载音频、转 MP3,直接进入
现有 Pop2Piano 出片流程。极大降低「先找歌、再下载、再上传」的门槛。
1. 用户流程(UX)
上传页
┌─────────────────────────────────────────────┐
│ 选择风格:钢琴现场 / Lo-fi / 动漫 / 抒情 │
│ │
│ ① 拖入 MP3 ② 粘贴 YouTube 链接 ▶ │ ← 新增②
│ [____________] [https://youtu.be/…] [开始]│
└─────────────────────────────────────────────┘
│ 粘贴链接 + 开始
▼
处理页(已有动画)
├ 解析链接 / 读取信息(标题、时长)
├ 下载音频(yt-dlp) ~5–30s
├ 转 MP3(ffmpeg,已装)
└ Pop2Piano 编曲(已有) ~15–60s
▼
工作室(竖屏 / 副歌精华 / 预设 / 慢速 / 原声 / 导出)
成品和上传 MP3 完全一致:可播原声、加载人声分轨、导出竖屏副歌片段 / 谱子 / 高音质音频。
2. 技术架构
复用现有「后端写 web/samples/,前端 serve」模式(/separate 已是这么做的)。
前端(9000) ──POST /youtube {url, style}──▶ 后端(9001)
│ 1. yt-dlp 取信息 → 校验时长 ≤ 上限
│ 2. yt-dlp 下载 bestaudio
│ 3. ffmpeg → web/samples/yt_<id>.mp3
│ 4. pop2piano_engine.arrange_pop2piano()
▼
前端 ◀──{ tracks, bpm, theme, title,
audioUrl: "samples/yt_<id>.mp3" }──┘
→ loadMultitrack(data, data.audioUrl) → show('studio')
关键点:返回 audioUrl 指向 samples/yt_<id>.mp3(前端服务器 serve),
于是「原声播放 / 🎤加载人声分轨」自动可用 —— 和上传 MP3 路径一模一样。
3. 后端实现
3.1 依赖
pip install yt-dlp(venv 内)- ffmpeg 已装(
/opt/homebrew/bin/ffmpeg) - yt-dlp 需要能频繁升级(YouTube 改版就会失效)→ 记录在 requirements,部署时定期
-U
3.2 新端点 POST /youtube(app.py)
@app.post("/youtube")
async def youtube_endpoint(payload: dict = Body(...)):
url = payload["url"]
style = payload.get("style", "piano_live")
import youtube_service as Y # 新文件
info = Y.probe(url) # 取 title/duration/id,不下载
if info["duration"] > MAX_YT_SEC: # 时长上限(成本/滥用)
raise HTTPException(400, f"视频过长({info['duration']}s),上限 {MAX_YT_SEC}s")
mp3_path, web_url = Y.download_mp3(url, info["id"]) # 下载 + 转码 + 缓存
import pop2piano_engine as PE
result = PE.arrange_pop2piano(mp3_path, style=style, max_sec=None, title=info["title"])
result["audioUrl"] = web_url # "samples/yt_<id>.mp3"
return JSONResponse(result)
3.3 新文件 youtube_service.py
probe(url) → yt-dlp -J(dump-json,不下载)取 id/title/duration/is_live- 校验:拒绝直播
is_live、私享/会员、时长超限 download_mp3(url, vid):- 缓存:若
web/samples/yt_<vid>.mp3 已存在 → 直接返回(同一视频不重复下) yt-dlp -f bestaudio -x --audio-format mp3 --audio-quality 0 -o web/samples/yt_<vid>.%(ext)s URL- 返回本地路径 +
samples/yt_<vid>.mp3
- 错误归一化:网络失败 / 地区限制 / 年龄限制 / 解析失败 → 明确中文报错
3.4 配置
MAX_YT_SEC = 8 * 60 # 8 分钟上限(可调)
4. 前端实现
4.1 上传页(index.html)
拖拽区下方加一个「或粘贴链接」行:
<div class="yt-row">
<input id="ytUrl" class="yt-input" placeholder="粘贴 YouTube 链接,例如 https://youtu.be/..." />
<button id="ytGo" class="btn primary">▶ 下载并制作</button>
</div>
4.2 逻辑(app.js)
async function handleYouTube(url) {
const finish = startProcAnim();
try {
const res = await fetch(CONFIG.BACKEND_URL + '/youtube', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({ url, style: selectedStyle })
});
if (!res.ok) throw new Error((await res.json()).detail || ('HTTP '+res.status));
const data = await res.json();
finish();
if (data.theme && Themes[data.theme]) Visualizer.setTheme(Themes[data.theme]);
loadMultitrack(data, data.audioUrl); // 原声 + 分轨都可用
show('studio');
} catch (e) { finish(); alert('下载/处理失败:' + e.message); }
}
- URL 简单校验(含
youtube.com / youtu.be) - 「🎤加载人声」:当前
/separate 收 File;YouTube 无 File → 改成「无 File 时 fetch
audioUrl 成 blob 再发」(getOriginalAudio() 已会 fetch audioUrl,小改即可)
5. 风险与规避(重要)
| 风险 | 说明 | 规避 |
|---|
| ⚠️ 法律 / 版权 / ToS | YouTube 服务条款禁止未经授权下载;歌曲多有版权。商用上线这是头号风险 | ① 本地验证阶段无碍;② 上线前定位为「用户对自己有权的内容」并加用户声明/勾选;③ 或只做「链接→你本地处理」不在服务端留存 |
| 服务端 IP 被封 | YouTube 会封数据中心 IP;你本地 Mac(住宅 IP)现在没问题,部署到云大概率失败 | 生产需 cookies / 住宅代理;先本地跑通,部署另算 |
| yt-dlp 易失效 | YouTube 改版会让下载报错 | 固定版本 + 定期 pip install -U yt-dlp;报错友好提示 |
| 成本 / 滥用 | 下载+处理任意长视频耗资源 | 时长上限(8 min)、按视频 id 缓存、生产加限流 |
| 延迟 | 下载+编曲 30–90s,同步请求会卡住 UI | MVP 用现有处理动画顶住;生产改异步任务+轮询(见 Phase 3) |
6. 分期落地
| 阶段 | 内容 | 估时 | 产出 |
|---|
| Phase 1 · 本地 MVP | 装 yt-dlp;/youtube 同步端点(时长上限+按id缓存);前端链接输入框+处理动画 | ~半天 | 你 Mac 上:粘链接→出片 端到端跑通 |
| Phase 2 · 健壮性 | 错误态(无效/直播/年龄限制/超长);「加载人声」走 audioUrl;进度提示;取消 | ~半天 | 体验顺滑、报错清晰 |
| Phase 3 · 异步任务 | 任务队列 + /youtube/status 轮询,长视频不阻塞 | ~1 天 | 可处理较长视频、可多任务 |
| Phase 4 · 生产 | cookies/代理破 IP 封锁、限流、法律声明/勾选、对象存储、清理定时任务 | 上线前 | 可对外服务 |
7. 待你决策
- 法律姿态:现在只做本地验证(推荐,先跑通看效果),还是直接按可上线设计(要加声明/限制)?
- 时长上限:默认 8 分钟够吗?(越长越慢越贵)
- 是否现在就实现 Phase 1?(我可立刻装 yt-dlp + 写端点 + 前端输入框,在你 Mac 上跑通)
</content>
</invoke>
14 · 最终形态与可视化方案发散
回答四个问题:最终结果是什么?只有"落音符"吗?歌词/原视频可行吗?现在流行什么?
Q4. 现在市面上的主流方案(先看全景)
| 方案 | 代表 | 卖点 | 适合目标 |
|---|
| ① 落音符 / Synthesia | Rousseau、Patrik Pietschmann、Kassia | 俯视键盘+彩色下落音符+粒子,演奏感强、可弹、解压 | 钢琴翻弹(当前主流,20亿+播放) |
| ② 真人手+落音符叠加 | 多数钢琴 up 主 | 真实双手 + Synthesia 视觉,可信度高 | 教学 / 炫技 |
| ③ 歌词视频 (Lyric Video) | 海量官方/二创 | 动态歌词 + 背景,制作轻、传唱强 | 情感 / 传播 |
| ④ 分屏对比 (Split-screen) | 原 MV vs 翻弹/反应 | 原视频做"注意力锚点",实测 +约4% 互动 | 反差 / 关注 |
| ⑤ 波形/频谱可视化 | Spotify Canvas 风 | 通用、任意音频可用,制作极轻 | 单曲循环 / 氛围 |
| ⑥ 3D/粒子/AI 生成视觉 | 新一代可视化工具 | 沉浸、drop 时粒子爆发、跟随能量 | 电子/氛围乐 |
| ⑦ 氛围静态场景 | "relaxing piano" 频道 | 雨窗/壁炉/咖啡馆 + 音频,极致氛围 | 放松/学习 BGM |
趋势:2026 行业整体在从"2D 波形贴静图"往 3D、粒子、drop 爆发、实时可交互 走;
同时歌词视频因门槛低、传唱强,仍是最大的内容池之一。
Q2. 只有"落音符"这一种吗?—— 不是
落音符是钢琴翻弹品类里最优的(展示"弹了什么"、解压、有教学感),PianoReel 已做到位。
但它只解决"演奏感"。换个目标,更优解就变了:
- 想要情感/虐心/传唱 → 歌词模式 > 落音符
- 想要关注/反差 → 原 MV 分屏 > 落音符
- 想要氛围/放松 BGM → 静态美学场景 > 落音符
结论:不是替换,而是把落音符当基底,做成"可叠层的场景",按目标切模式。
Q3. 歌词 / 加入原视频,可行吗?—— 都可行,且歌词是高价值差异点
歌词(强烈建议做,门槛中等、版权风险低)
- 拿到歌词+时间轴的三条路:
- 用户贴
.lrc(最准、最省) - 歌词 API 拉取(需匹配,可能不准)
- ★对人声轨跑 Whisper 自动转写+逐词时间戳(我们已经用 Demucs 分出人声,直接喂 Whisper 即可,零额外素材)
- 呈现:在落音符场景上叠卡拉OK式逐字高亮字幕,或纯歌词模式(动态排版)。
- 价值:传唱+情感,竖屏短视频里歌词几乎是"标配加成";技术链路我们已具备一半。
加入原视频(技术易、版权需谨慎)
- 技术:yt-dlp 现在只下音频,同样能下视频;ffmpeg 可合成:
- 分屏:上原 MV + 下落音符(天然适配 9:16)
- 背景:原视频压暗做底,落音符浮在上层
- ⚠️ 版权:用原 MV 画面比用音频风险更高(叠加了视觉版权)。本地验证无碍;
商用需授权/限定自有内容。建议作为"本地/受限"能力,不默认开。
Q1. 最终结果应该是什么(北极星)
一键把任意歌 → 一条可直接发布的竖屏钢琴短视频。
不止一种视觉,而是一个可叠层的场景,按创作目标切"模式"。
场景分层(基底 + 可选叠层):
[ 基底 ] 落音符 + 88键 + 主题配色 ← 招牌,已完成
[叠层] 歌词(卡拉OK高亮) ← 建议下一步
[叠层] 片头标题卡 / 封面文案 ← 轻量、提转化
[叠层] 原视频(分屏 / 背景,受限) ← 技术易、版权慎
[调校] 竖屏 · 副歌精华 · 情感慢速 · 高级感预设 ← 已完成
四种"出片模式"(同一首歌,一键切):
| 模式 | = 哪些层 | 目标 |
|---|
| 🎹 演奏模式 | 落音符(现状) | 演奏感 / 可弹 |
| 🎤 歌词模式 | 落音符 + 卡拉OK歌词 | 情感 / 传唱 |
| 🎬 对比模式 | 原 MV 分屏 + 落音符 | 反差 / 关注 |
| 🌙 氛围模式 | 美学背景 + 极简音符 + 音频 | 放松 BGM |
每首歌最终交付:MP4(视频)+ 谱子 + 高音质音频,开箱即发。
✅ 实现状态(已全部落地)
四种出片模式 + 片头标题卡均已实现并验证(见 studio「出片模式」切换器):
- 🎹 演奏 / 🌙 氛围(光斑+暗角)/ 🎤 歌词(Whisper 逐词卡拉OK,支持贴 LRC + 语言选择)
- 🎬 对比(原视频,分屏 / 背景两种布局)
- ➕ 片头标题卡(开头淡入淡出)、📷 封面 PNG 导出
- 导出统一为 MP4;四模式均已验证可正确入画
后端新增:/lyrics(faster-whisper)、/youtube_video、/transcode。
后续可继续打磨(非阻塞)
- 🎤 歌词模式(卡拉OK) —— 复用已分离的人声轨 + Whisper 逐词时间戳,
在场景上叠高亮歌词。最高性价比差异点,版权风险低。
- 🎬 片头标题卡 + 封面文案 —— 轻量、直接提升完播与点击。
- 🎬 对比/背景模式(原视频) —— 技术易(yt-dlp 视频 + ffmpeg 合成),
作为本地/受限能力先验证观感。
- 🌙 氛围模式 —— 一组美学背景预设,覆盖"放松 BGM"大池子。
</content>
15 · 当前服务清单 · 本地 App 打包 · 发布上线
回答三件事:现在用了哪些服务 / 打包成本地 App 要做什么 / 发布上线要做什么。
1. 当前运行时全景(用到哪些服务)
两个进程
- 前端:静态站,端口 9000(现在用
python -m http.server,任意静态服务器都行) - 后端:FastAPI + uvicorn,端口 9001
Python 库(⚠️ 现 requirements.txt 不全,缺下列重依赖)
| 用途 | 库 |
|---|
| Web 框架 | fastapi · uvicorn · python-multipart |
| 编曲(主引擎) | transformers + torch(Pop2Piano) |
| 分轨 | demucs(torch) |
| 歌词 | faster-whisper(CTranslate2) |
| 转写(旧路径) | basic-pitch(macOS CoreML / Linux onnxruntime) |
| 音频/谱 | librosa · music21 · pretty_midi |
| YouTube | yt-dlp |
系统级外部二进制(非 pip,要单独装)
ffmpeg(转码 / 音频)· fluidsynth(高音质音频)· lilypond(PDF 谱子)
数据 / 模型
backend/soundfonts/piano.sf2 钢琴音色 141MB(gitignore)- 首次运行联网下载:Pop2Piano 模型、Whisper small(~250MB)、Demucs 模型(缓存到
~/.cache)
前端外部 CDN(联网依赖)
@tonejs/midi、soundfont-player(后者还在线拉 MusyngKite 钢琴采样)→ 离线/打包要本地化
外部 API:MiniMax(仅实验性 LLM 编曲路径,主流程不用)· 出站访问 YouTube
2. 打包为本地 App(自用)
难点:ML 栈很重(torch + 多模型,几个 GB),外部二进制要随包带上。
方案 A · 最省事:脚本 / Docker(推荐先做)
- 一个
start 脚本(或现有 make dev)一键起前后端 + 开浏览器。 - 或
docker compose up:仓库已有后端 Dockerfile,把 ffmpeg/fluidsynth/lilypond/模型都装进镜像,一条命令跑通、换机可复现。 - 不是"原生 App",但自用完全够。
方案 B · 真·桌面 App:Tauri/Electron + Python sidecar
- Tauri(轻) 或 Electron 包前端 UI,内嵌打包好的 Python 后端(PyInstaller)作为 sidecar。
- 产出 macOS
.app / Windows .exe。体积大(torch 打包后约 1–3GB)。
不管 A 还是 B,待办清单
- 补全
requirements.txt(torch/transformers/demucs/librosa/music21/pretty_midi)并钉版本 - 外部二进制随包带(或首启检测 + 引导安装 brew/winget)
- 模型缓存目录固定 + 首启下载进度提示
- 前端去 CDN:把
@tonejs/midi、soundfont-player 和钢琴采样本地化(否则离线打不开) - 后端端口动态化 + 前端
BACKEND_URL 自适应 - (桌面 App)macOS 代码签名/公证过 Gatekeeper
3. 发布上线(对外服务)
技术只是一部分,法务和成本才是真门槛。
技术部署
- 前端:静态托管。首选 Cloudflare Pages(免费档可商用、流量无限、同账号打通 R2/Workers);Vercel/Netlify 亦可,但免费档禁止商用(收费要 Vercel Pro $20/月)。对比见 docs/17。
- 后端:容器化上云。ML 推理重 → 需要足够 CPU/内存,理想 GPU。
- 异步任务队列(Celery / RQ + worker 池):编曲/分轨/识别是秒级~分钟级,不能在请求里同步跑,要排队 + 可扩容。
- 对象存储(S3 / OSS):生成的 mp3/mp4/分轨/封面外置。
- YouTube 下载:云数据中心 IP 会被封 → 需 cookies / 住宅代理(docs/13)。
产品(都没建)
安全 / 运维
- CORS 收紧到自有域名 · 限流 · 输入校验(
/youtube /transcode 现收任意输入)· 密钥管理 · 日志/监控 · 自动伸缩。
⚠️ 法务(头号风险)
- 翻弹 = 作曲版权;对比模式 = 画面 + 录音版权。对外服务 = 你在帮用户批量生成可能侵权内容 → 平台责任(DMCA/避风港)、ToS、内容政策、可能要授权曲库合作。没解决前不建议公开商用。
成本
GPU/算力 + 存储 + 带宽 + 代理 + 可能的授权费。
一句话
- 自用 → 先做 Docker/脚本一键启动(半天);要原生 App 再上 Tauri(几天)。
- 发布 → 技术(队列/存储/GPU/代理)+ 产品(账号/付费)+ 法务(版权) 三座山,法务是前提。
</content>
16 · 分发与收费方向(持续思考用)
这款产品怎么发出去、怎么收钱。本文是开放讨论的备忘,会持续更新。
两个主导约束贯穿全篇:① ML 算力成本 · ② 版权责任。
相关:本地打包/部署见 15-packaging-deploy.md,状态见 00-status.md。
一、三条分发主路
| 方向 | 算力谁付 | 你的法律身份 | 触达 | 变现 |
|---|
| A. 本地桌面 App(Electron/Tauri) | 用户机器(你≈0) | "工具"(像剪辑软件/OBS) | 要安装+要好机器 | 买断 / 订阅+授权 |
| B. Web SaaS(云) | 你付 GPU($$$) | "平台"(托管+处理内容) | 浏览器即用,最广 | 订阅 / 点数 |
| C. 内容号(自己产片) | 你本地 | 创作者(逐条担版权) | 看你做号 | 流量/广告/接广 |
三个决策轴
- 算力成本:云=每单 GPU 烧钱;本地=0(用户出)。
- 法律暴露:SaaS 你托管+处理+服务端下 YouTube → 责任最大;桌面 App 你只是"工具" → 平台责任低得多;内容号逐条自担。
- 触达/体验:SaaS 免安装最广;桌面 App 有安装+硬件门槛;内容号不卖软件。
判断:桌面 App(local-first)同时绕开了两个最大问题(算力成本 + 平台版权责任),是最"干净"的软件产品路。代价是体积大、要好机器、更新分发麻烦。
建议序列(不要一上来就选终局):
先:内容号验证需求(零基建)
→ 再:本地桌面 App 卖给创作者(低法务/低成本)
→ 视情况:需求大 + 有钱投基建 + 做合规,再上云 SaaS
二、桌面 App 怎么收费(Electron 路线)
本质:程序跑在用户机器上,卡不了服务器 → 靠 许可证(license)门控:用户买"激活码",App 联网校验。
1. 收费模式
| 模式 | 说明 | 适配 |
|---|
| Freemium(免费+解锁) | 免费版有限制,付费解 Pro | 最适合 —— 天然付费点:去水印 |
| 订阅 | 月/年,定期联网校验 | 收入稳,桌面订阅接受度略低 |
| 买断 | 付一次永久用(大版本另收) | 好卖,但无复购 |
PianoReel 的天然分层
- 免费版:带 PianoReel 水印 · 720p · 基础模式
- Pro:去水印 · 1080p/4K · 全部出片模式+特效 · 批量 · 商用授权
2. 技术怎么落地(别自己造支付/发证)
- 支付+发证一体(最省事):Lemon Squeezy / Paddle / Gumroad
- 付款 → 自动生成 license key → App 调 API 校验。
- ⭐ Lemon Squeezy / Paddle 是 Merchant of Record:帮你处理全球税务/VAT/退款,独立开发者卖全球这点价值极大。
- 专业发证/激活(机器绑定/额度控制更强):Keygen.sh / Cryptolens + Stripe 收款。
- App Store(Mac/Microsoft):帮你收款+防盗版,但抽 15–30%,且沙盒严格 —— 本应用带 Python+ffmpeg+模型下载,Mac App Store 沙盒大概率装不进 → 官网直接下载分发更现实。
3. 几条现实
- 破解防不死:桌面 license 校验总能被绕。目标不是防住所有人,而是让随手白嫖不方便 —— 在线激活 + 每隔几天复校验(留离线宽限期)+ 把部分高级功能绑云。
- License 服务器很轻很便宜:只发码/校验,不是 GPU 烧钱后端。这正是桌面路的好处:收费基建轻。
4. 加分玩法:Local-first + 少量云
桌面 App 本地干重活(免费也能用),把杀手级增值放云上:云端 4K/批量渲染、云端授权曲库。
- 好处:① 天然防破解;② 给"订阅"真实理由;③ 云只在付费时烧钱,成本可控。
三、落到最小可行(MVP 收费)
免费下载(带水印)获客 → Lemon Squeezy 卖 Pro license(去水印+高清+全功能)→ App 在线激活校验。
税务平台扛、license 服务器极轻、不碰 GPU 账单。
为将来预留:现在就可以在产品里把 Pro 功能位标出来(哪些免费、哪些付费),先不收费,等分发路定了再接 license。
四、版权红线(无论哪条路)
- 公开发布默认走纯钢琴模式(演奏/氛围/歌词)—— 只有用户自己的钢琴演奏,是标准 cover 形态,平台最被接受。
- 对比模式(原 MV 画面 + 原录音) = 高风险:模糊/调小声无效(Content ID 对两者都顽强),定位为"自己看 / 拿到授权再用"。
- 做软件工具(桌面 App)时你是"工具方",责任远小于"平台";做 SaaS 时你托管+处理用户内容,需要 ToS / 内容政策 / 可能授权曲库。
待定问题(持续思考)
- 终局想做:卖工具 / 自己做号 / 做平台?
- 规模:小而美自养 vs 冲规模可能融资?
- 收费:买断 vs 订阅 vs Freemium?先标 Pro 位还是直接接 license?
- 愿意碰版权授权/合规到什么程度?
</content>
17 · 上线发布指南(新手向 · 一步步怎么发)
面向完全新手:每一块发布到哪、怎么发、选哪台机器、要设置什么。
不要一次全做 —— 按下面的「分阶段」来,每阶段都能看到成果。
配套:架构总览见 15-packaging-deploy.md,收费见 16-distribution-monetization.md。
0. 先看全局:4 块东西,各去哪
用户浏览器
│
▼
pianoreel.com
┌─────────────────────────────────────────────────────────┐
│ ① 前端(静态页) → Cloudflare Pages [免费] │
│ ② API/登录/付费 → Cloudflare Workers + D1 [~$5/月] │
│ ③ 文件存储(视频等) → Cloudflare R2 [按量·极便宜] │
│ ④ 重型 AI 编曲 → Modal (GPU) [按秒·每首几日元] │
│ 收款 → Stripe [抽 2.9%+$0.3/笔] │
└─────────────────────────────────────────────────────────┘
- ① 前端:你现在的
prototype/web(纯静态)→ 直接挂 Cloudflare Pages。 - ④ AI 编曲:Pop2Piano/Demucs/Whisper(要 GPU)→ 放 Modal。这是本文重点。
- ②③ 账号/付费/存储:要做成"对外收费产品"时才需要;自己用不用碰。
新手最重要的一句话:先只做 ①(前端上线)+ ④(AI 上 Modal),让"任何人打开 pianoreel.com 就能用"。②③(账号/付费)等有人用了再加。
阶段 1 · 把前端挂上线(最简单,先做这个)
目标:pianoreel.com 打开就是你的工作室界面(纯前端,不含 AI 也能看 UI、玩示例)。
前端是什么 + 放哪家
当前前端是纯 Vanilla JS(原生 JS,无框架、无打包) —— HTML + CSS + Canvas + Web Audio,就是一堆静态文件,任何静态托管都能放。
| 托管 | 免费档 | 商用(免费档) | 和你的 R2/Workers | 备注 |
|---|
| Cloudflare Pages ⭐ | 流量无限 | ✅ 允许 | 同账号无缝 | 你全家桶在 CF,首选 |
| Vercel | ~100GB/月 | ⚠️ 不允许,要 Pro $20/月 | 跨平台 | 它强在 Next.js,你用不到 |
| Netlify | ~100GB/月 | 有限制 | 跨平台 | 一般 |
| GitHub Pages | 软上限 | 个人项目可 | 跨平台 | 最简陋 |
→ 建议 Cloudflare Pages:你前端是纯静态,Vercel 的 Next.js 优势用不上,反而 Vercel 免费档禁止商用(一收费就得交 $20/月),而 Cloudflare Pages 免费档就能商用、流量无限、和域名/R2/Workers 同账号。Vercel 不是不能用,只是对你这套不划算。
步骤
- 注册域名:Cloudflare 或 お名前.com 拿下
pianoreel.com,把 DNS 交给 Cloudflare 管。 - Cloudflare Pages(dash.cloudflare.com → Workers & Pages → Create → Pages):
- Connect to Git → 选你的
pianoreel 仓库。 - 构建设置:
- Framework preset:
None - Build command: 留空(纯静态,不用构建)
- Build output directory:
prototype/web
- 点 Save and Deploy → 几十秒后给你一个
xxx.pages.dev 网址。
- 绑定域名:Pages 项目 → Custom domains → 加
pianoreel.com(Cloudflare 会自动配好 DNS + HTTPS)。
✅ 完成后:打开 pianoreel.com 就是你的站,免费、自动续期 HTTPS、全球 CDN。
此时"上传音频/YouTube"等需要后端的功能还不能用(后端在阶段 2 上)。
阶段 2 · 把 AI 后端发布到 Modal(重点)
目标:让前端能真正"上传歌 → AI 编曲出片",后端跑在云 GPU 上、按秒计费、没人用就停。
2.1 注册 + 装工具(5 分钟)
- 去 modal.com 注册(用 GitHub 登录即可),有 每月 $30 免费额度。
- 本机装 CLI:
```bash
pip install modal
modal token new # 浏览器弹出授权,点确认即可
```
2.2 选哪台机器(GPU)
| GPU | 显存 | 价格(约) | 给你的建议 |
|---|
| T4 | 16GB | ~$0.6/小时 | ✅ 首选 —— Pop2Piano/Demucs/Whisper 都够跑,最便宜 |
| L4 | 24GB | ~$0.8/小时 | 想快一点点可选 |
| A10G | 24GB | ~$1.1/小时 | 更快,没必要 |
| A100 | 40GB+ | ~$3.4/小时 | ❌ 杀鸡用牛刀,别选 |
→ 就选 T4。一首歌处理几十秒,单价几日元,缩容到 0 没人用不花钱。
2.3 写一个 Modal 部署文件
在 prototype/backend/ 下新建 modal_app.py(把现有 FastAPI 直接搬上去):
import modal
app = modal.App("pianoreel-api")
# 镜像:系统二进制 + Python 依赖 + 你的代码
image = (
modal.Image.debian_slim(python_version="3.11")
.apt_install("ffmpeg", "fluidsynth", "lilypond") # 系统工具
.pip_install( # ⚠️ 补全所有依赖
"fastapi", "uvicorn", "python-multipart",
"torch", "transformers", "demucs",
"faster-whisper", "librosa", "music21", "pretty_midi",
"yt-dlp", "basic-pitch",
)
.add_local_dir(".", remote_path="/app") # 把 backend 代码拷进去
)
# 模型缓存盘:让 Pop2Piano/Whisper/Demucs 只下载一次(重启不丢)
cache = modal.Volume.from_name("pr-models", create_if_missing=True)
@app.function(
image=image,
gpu="T4", # ← 选 T4
timeout=600, # 单次最长 10 分钟
scaledown_window=300, # 闲置 5 分钟后关机(缩容到 0 = 省钱)
volumes={"/root/.cache": cache}, # HF/模型缓存挂到 Volume
)
@modal.asgi_app()
def fastapi_app():
import sys
sys.path.insert(0, "/app")
from app import app as fastapi # 复用你现有的 FastAPI(app.py 里的 app)
return fastapi
2.4 发布
cd prototype/backend
modal deploy modal_app.py
跑完会给你一个公网地址,形如:
https://你的用户名--pianoreel-api-fastapi-app.modal.run
这就是你的线上后端 URL。
2.5 让前端指向它
把 prototype/web/js/core/config.js 的 BACKEND_URL 改成上面那个 Modal 地址:
BACKEND_URL: 'https://你的用户名--pianoreel-api-fastapi-app.modal.run',
然后 make bump(升版本号)→ 重新部署 Pages(push 到 Git 自动重发)。
✅ 完成后:pianoreel.com 上传音频 → 走 Modal GPU 编曲 → 出片。
2.6 这一步必须改的两件事 ⚠️
- 输出别再写本地
web/samples/ —— 云上没有"前端同目录"。要把生成的 mp3/mp4/谱子上传到 R2(见阶段 3),返回 R2 的 URL。改动点:stem_service.py / youtube_service.py / /sheet /render 现在写本地,要改成写 R2。 - YouTube 下载在云上会被封 IP —— 数据中心 IP 下 YouTube 多半 403。要么配 cookies / 住宅代理,要么 YouTube 这条路只在本地保留。
2.7 要设置的东西(Modal 后台)
- Secrets(密钥):
modal secret create pianoreel-secrets MINIMAX_API_KEY=xxx,然后函数里加 secrets=[modal.Secret.from_name("pianoreel-secrets")]。 - Volume:上面
pr-models 已自动建,存模型缓存。 - CORS:后端已
allow_origins=["*"],上线后收紧到 https://pianoreel.com(改 app.py 的 ALLOWED_ORIGINS)。
阶段 3 · 文件存储 → Cloudflare R2
目标:生成的视频/音频/封面存云上,前端能下载。
- Cloudflare dash → R2 → Create bucket(如
pianoreel-files)。 - 建 API Token(R2 → Manage API Tokens),拿到
access_key / secret_key / endpoint。 - Modal 后端用 boto3(S3 兼容)把结果传 R2:
```python
import boto3
s3 = boto3.client("s3", endpoint_url=R2_ENDPOINT,
aws_access_key_id=KEY, aws_secret_access_key=SECRET)
s3.upload_file(local_mp4, "pianoreel-files", "out/xxx.mp4")
```
- 让 bucket 走公开域名(R2 → Settings → Public access,或绑
files.pianoreel.com),返回该 URL 给前端。
💡 R2 的杀手锏:出站流量 0 元(S3 要收)——你存视频、用户下载,不花下载流量钱。
阶段 4 · 账号 + 付费(要收费时才做)
目标:登录、订阅、配额、收款。这是最"产品化"的一块,工作量也最大。
| 需求 | 用什么 | 怎么做 |
|---|
| API / 登录逻辑 | Cloudflare Workers | 写边缘函数;或接 Clerk / Supabase Auth 省事 |
| 用户/订阅数据 | Cloudflare D1(SQLite) | 存 user、plan、quota |
| 任务排队 | Cloudflare Queues | 前端请求入队 → 调 Modal → 写 R2 → 通知前端 |
| 收款 | Stripe | 建 Product/Price;Workers 接 Stripe Webhook 改用户订阅状态 |
| 免费/Pro 区分 | 前端 + 后端校验 | 免费=带水印/720p;Pro=去水印/高清/全功能 |
这一步建议最后做,而且可以先用现成 Auth(Clerk)+ Lemon Squeezy(帮你扛全球税)省一半工。
5. 成本总表(人民币 / 美元 / 日元)
| 项目 | 起步成本 | 说明 |
|---|
域名 .com | ~¥70/年($10 / ¥1,500/年) | Cloudflare 成本价 |
| Cloudflare Pages(前端) | 免费 | 静态站够用 |
| Cloudflare Workers(API) | $5/月 ≈ ¥36 / ¥750 | 要做账号/付费时才订 |
| Cloudflare R2(存储) | 几乎免费(10GB 内) | 出站 0 元 |
| Modal(GPU 编曲) | 每月 $30 免费额度 | 每首约 $0.02–0.05 ≈ ¥0.2 / 3–8 日元 |
| Stripe(收款) | 无月费 | 抽 2.9% + $0.30/笔 |
结论:
- 自己用 / 验证 → 本地跑就行,¥0。
- 小规模上线(前端 + Modal)→ 基本免费(吃 Modal $30 额度),每首歌几日元。
- 正式收费产品 → 加 Workers($5/月) + R2(几块) + Stripe(抽成),月十几美元封顶 + GPU 按量。
6. 推荐的发布顺序(新手照这个走)
- 阶段 1:域名 + 前端上 Cloudflare Pages →
pianoreel.com 能打开(半小时,免费)。 - 阶段 2:AI 后端上 Modal(选 T4)→ 上传歌能真出片(半天)。
- 阶段 3:输出改写 R2 → 文件可下载(接在阶段 2 里一起改)。
- 阶段 4:要收费了再加 账号 + Stripe(另起一摊)。
先把 1+2 跑通,你就有一个"任何人能打开、能出片"的线上 demo 了。
7. 三个必须记住的坑
requirements.txt 不全 —— 上 Modal 前补齐 torch/transformers/demucs/librosa/music21/pretty_midi(上面镜像里已列)。- YouTube 云端被封 IP —— 需 cookies/代理,否则云上
/youtube 会 403。 - ⚠️ 版权(头号风险) —— 对外服务 = 帮用户批量生成可能侵权内容。公开默认走纯钢琴模式;上线前要有 ToS / 内容政策。见
16。
</content>
18 · 运作机制详解 · 每个组件在哪 · 上云 · 成本
把"各个地方"讲透:**哪个组件跑在前端还是后端、数据怎么传、本地 vs 云有什么不同、
上云后各搬到哪、各自多少钱。** 新手也能看懂。
配套:上线步骤见 17-go-live-guide.md,收费见 16。
0. 一句话
重活(下载 / AI 编曲 / 转码 / 分轨 / 谱子)全在后端 Python;前端只负责界面、画落音符、放声、录像。
前端 ↔ 后端之间只用 HTTP 传两样东西:输入(URL 字符串 或 文件) 和 输出(音符 JSON + 文件 URL)。
PianoReel 有两套运行环境,逻辑一样,只有"文件怎么交付"那一处不同:
- 本地(开发/自用):前端 :9000 + 后端 :9001,共用同一个文件夹
web/samples。 - 云端(上线):前端在 Cloudflare、后端在 Modal、文件在 R2,没有共享文件夹 → 改用 R2 传文件。
1. 每个组件在前端还是后端
| 组件 | 在哪 | 干什么 | 文件 |
|---|
| yt-dlp | 后端 | 下载 YouTube 音频/视频 | services/youtube_service.py |
| ffmpeg | 后端 | 音频转码 mp3、WebM→MP4 | youtube_service / render_audio.py / /transcode |
| Pop2Piano | 后端 | AI 把整首歌编成钢琴多轨 | engines/pop2piano_engine.py |
| Demucs | 后端 | 分离原声 4 轨 | audio/stem_service.py |
| faster-whisper | 后端 | 识别歌词逐词时间戳 | services/lyrics_service.py |
| FluidSynth | 后端 | 音符→高音质钢琴 MP3 | audio/render_audio.py |
| music21 + LilyPond | 后端 | 音符→谱子 PDF/XML | services/sheet_service.py |
| —— | —— | —— | —— |
| 落音符动画 / 键盘 | 前端 | Canvas 每帧绘制 | visual/visualizer.js 等 |
| 钢琴声(合成) | 前端 | WebAudio 合成器发声 | audio/synth.js |
| 原声播放 | 前端 | <audio> 播 mp3 | core/app.js |
| 导出录制 | 前端 | MediaRecorder 录画面+声 | core/app.js(再送后端转 MP4) |
| MIDI 解析 | 前端 | Tone.js 直接读音符 | core/app.js(仅 MIDI 路径) |
| 多语言/UI | 前端 | i18n + DOM | core/i18n.js |
记忆法:凡是"要装 Python 库 / 系统工具 / 跑模型"的 → 后端;凡是"画图、出声、录屏" → 前端。
2. 三条输入路径的数据流(关键)
路径 A · 上传 MP3/音频 → 经后端 AI 编曲
前端: 拿到 File → FormData(file) ──POST /arrange──▶ 后端
Pop2Piano 编曲
前端 ◀── JSON {tracks(音符), bpm, theme, title} ──┘
loadMultitrack() → 画落音符 + 合成钢琴声
传的是文件本身;收的是音符 JSON。
路径 B · YouTube 链接 → 后端下载 + 编曲(= A 前面多一步下载)
前端: 一个 URL 字符串 ──POST /youtube {url,style}──▶ 后端
① yt-dlp 读标题/时长(校验)
② yt-dlp 下音频 → ffmpeg 转 mp3
存 web/samples/yt_<id>.mp3
③ Pop2Piano 编曲
前端 ◀── JSON {tracks, bpm, title, ┘
audioUrl:"samples/yt_<id>.mp3"} ──
loadMultitrack(data, audioUrl)
├ tracks → 画落音符
└ <audio src="samples/yt_<id>.mp3"> 播原声
前端只传一个 URL;处理页那几步(下载/转码/编曲)全在后端一次 HTTP 里完成,前端等待时只放处理动画。
路径 C · MIDI → 完全在前端,不碰后端
前端: 读 .mid 文件 → Tone.js 解析出音符 → 直接进工作室(秒级)
不经 yt-dlp / ffmpeg / Pop2Piano —— MIDI 本身就含音符。
所以:A 和 B 殊途同归(都到 Pop2Piano);C 独立(纯前端、无 AI、秒出)。
3. 输出 / 导出怎么走
| 导出 | 谁做 | 流程 |
|---|
| ⬇ 视频 MP4 | 前端录 + 后端转 | 前端 MediaRecorder 录 Canvas+声(WebM) → POST /transcode → 后端 ffmpeg 转 MP4 → 下载 |
| ⬇ 谱子 | 后端 | POST /sheet(传音符) → music21+LilyPond → PDF/XML |
| ⬇ 高音质音频 | 后端 | POST /render → FluidSynth → MP3 |
| ⬇ MIDI | 前端 | Tone.js 直接把音符写成 .mid |
| 📷 封面 PNG | 前端 | Canvas toDataURL 直接存图 |
4. 核心机制:文件怎么从后端"交"到前端
这是最容易卡住新手、也是本地 vs 云唯一的差异点。
本地:靠"共享文件夹"
- 后端生成的 mp3/视频,写到
prototype/web/samples/。 - 前端服务器(:9000)正好 serve 的就是
prototype/web/。 - 所以后端返回相对路径
samples/yt_<id>.mp3,前端用 <audio src="samples/yt_<id>.mp3"> 同源就能取到。 - 成立的前提:前后端在同一台机器、同一个目录。本地满足,所以跑得通。
云端:没有共享文件夹了 → 用 R2
- 上云后前端在 Cloudflare、后端在 Modal,两台机器、各自的硬盘,
samples/... 这个相对路径前端取不到。 - 解法:后端把文件上传到 Cloudflare R2,返回完整 URL(如
https://files.pianoreel.com/yt_xxx.mp3),前端从 R2 取。 - 要改的代码就这一处:
youtube_service / stem_service / render / sheet 现在"写本地目录" → 改成"传 R2 + 返回完整 URL"。其余逻辑不变。
本地: 后端写 web/samples/ → 返回 "samples/x.mp3" → 前端同源取 ✅
云端: 后端传 R2 → 返回 "https://files…/x.mp3" → 前端从 R2 取 ✅
5. 上云:每个组件搬到哪
pianoreel.com
│
┌───────────────────────┼─────────────────────────────┐
▼ ▼ ▼
① 前端(静态) ② API/登录/付费 ③ 文件
Cloudflare Pages Cloudflare Workers + D1 Cloudflare R2
(免费) + Stripe(收款) (存视频/音频,出站免费)
│ 入队 Queues
▼
④ 重型 AI 编曲/分轨/歌词
Modal (GPU, T4)
按秒计费,缩容到 0
| 组件 | 搬到 | 备注 |
|---|
前端 prototype/web | Cloudflare Pages | 纯静态,连 Git 自动部署 |
| 后端 FastAPI(全部 ML) | Modal(GPU T4) | 现有 FastAPI 几乎直接搬;见 docs/17 |
| 生成的文件 | Cloudflare R2 | 后端写 R2,前端从 R2 取 |
| 账号/订阅/配额 | Workers + D1 | 要收费时才做 |
| 收款 | Stripe | 抽成,Webhook 通知 Workers |
| 歌词模型(可选) | Workers AI 自带 Whisper | 也可继续放 Modal |
⚠️ YouTube 下载在云上会被封 IP(数据中心 IP→403)—— 见下一节的解决思路。
5.5 · ⭐ 候选目标架构:桌面轻 + 云端 ML(🟡 考虑中,未定)
纯 SaaS(全云)和纯桌面(全本地)各有短板。下面这套混合架构很可能是最优折中,
目前仍在考虑阶段、未拍板,先记录下来。
思路
桌面 App 负责"轻活 + 下载",云端只负责"重 ML"。
桌面 App(用户机器, 住宅 IP) 云端(Modal GPU)
───────────────────────── ──────────────
· UI / 落音符渲染 / 播放
· yt-dlp 下载 YouTube ← 住宅IP, 不被封 ✅
· ffmpeg 转 mp3(轻)
│ 上传 mp3 ────────────────────▶ · Pop2Piano 编曲
│ ◀── 音符 JSON + 生成文件 ────── · Demucs 分轨
· 导出 MP4(本地录+转, 轻) · Whisper 歌词
(重 ML, 要 GPU)
关键:云端不再碰 YouTube,只收一个音频文件(用户上传的、或桌面下好的,云端不在乎来源)——
这正是现有 MP3 路径的逻辑,后端几乎不用改。
为什么这套好(三个问题一起解决)
| 问题 | 解决 |
|---|
| YouTube 云端被封 IP | 下载在用户机器(住宅 IP) → 天生没问题 ✅ |
| 用户没 GPU / 不想下几 GB 模型 | 重 ML 在云端,用户机器只干轻活 ✅ |
| 怎么收费 / 防破解 | 重 ML 在你服务器 → 天然防破解,正好做付费墙 ✅ |
轻 / 重怎么切(只有 3 个 ML 必须留云)
| 组件 | 轻/重 | 放哪 |
|---|
| yt-dlp 下载 | 轻(需住宅IP) | 桌面 |
| ffmpeg / FluidSynth / LilyPond | 轻(CPU) | 桌面 或 云端 |
| 渲染 / 播放 / 导出 | 轻 | 桌面 |
| Pop2Piano / Demucs / Whisper | 重(GPU) | 云端(付费墙) |
→ 云端只为"编曲那几十秒 GPU"付钱,成本压到最低。
YouTube 下载在三种分发路下分别怎么处理
| 分发路 | YouTube 下载 | 说明 |
|---|
| 纯桌面 App | 用户本地下(住宅IP) | 天然没问题 |
| 纯 Web SaaS | 云服务器下 | 被封,要住宅代理($/GB)或改"用户上传文件" |
| 桌面轻 + 云端 ML(本节) | 用户本地下 | 没问题 + 重 ML 仍可控可收费 |
还要注意(都不难)
- 桌面要打包 yt-dlp + ffmpeg 二进制(Electron 里带上)。
- 桌面↔云端加认证/license —— 只有付费用户能调你的 GPU(别被白嫖算力)。
- 桌面把 mp3 上传到云,一首几 MB,很快。
- 重 ML 仍按首付 GPU 钱(每首几日元)—— 这正是要收费的部分,合理。
取舍小结
- 纯 SaaS:触达最广,但 YouTube 要代理、用户内容你托管(版权暴露大)、GPU 全你付。
- 纯桌面:法务/算力都轻,但要求用户硬件、模型几 GB、ML 在本地难收费防破解。
- 桌面轻 + 云端 ML(本节):绕开 YouTube 封锁 + 不要求用户硬件 + 云端 ML 天然可收费防破解,是三者折中。
🟡 状态:考虑中,未定。 真要做的话,现有后端基本可直接当"云端 ML 服务",桌面那层是新增的壳 + 把 yt-dlp/ffmpeg 搬到本地。
6. 成本详解(人民币 / 美元 / 日元)
| 项目 | 价格 | 折算 | 何时花 |
|---|
域名 .com | ~$10/年 | ¥70 / ¥1,500 一年 | 一次性 |
| Cloudflare Pages(前端) | 免费 | — | 永久 |
| Modal(GPU 编曲) | 每首 ~$0.02–0.05 | ¥0.2 / 3–8 日元 一首 | 按用量;送 $30/月免费额度 |
| R2 存储 | $0.015/GB·月 | ¥0.1 / 1.6 日元 每 GB | 量大才有感;10GB 内免费 |
| R2 出站(下载) | $0 免费 | — | 用户下载视频不花钱 |
| Workers(API) | $5/月 | ¥36 / ¥750 一月 | 要做账号/付费时才订 |
| Stripe(收款) | 2.9% + $0.30/笔 | $0.30 ≈ ¥2.2 一笔 | 有人付钱才扣 |
三个省钱要点
- GPU 缩容到 0:没人用就不开机,别让 GPU 常驻(常驻小卡 24/7 ≈ ¥3,000+/月,纯浪费)。
- R2 设生命周期自动删:视频用户下载完没用了,N 天后自动删 → 存储常年在免费额度内。
- R2 出站免费:媒体产品在 AWS 最贵的就是下载流量,R2 砍掉这块。
不同阶段总花费
| 阶段 | 月成本 |
|---|
| 自己用(一周一首) | ¥0 —— 本地 Mac 跑就行,不用云 |
| 小规模 demo(前端+Modal) | 基本免费(吃 Modal $30 额度),每首几日元 |
| 正式收费产品 | Workers $5/月 + R2 几块 + GPU 按量 → 月十几美元封顶 + 每首几日元 |
7. 现在能跑通吗?
- 本地:完全跑通 ✅(已端到端测过:贴 YouTube 链接 → 下载 → 编曲 → 工作室 → 导出 MP4)。
- 上云:逻辑一样,唯一必改的是第 4 节那处——把"写本地目录"换成"写 R2 + 返回完整 URL",再加 YouTube 代理。其余照搬。
一句话收尾:三件重活(yt-dlp / ffmpeg / Pop2Piano)全在后端,前端只画图放声;前后端用 HTTP 传 URL/文件和 JSON;本地靠共享目录交付文件,上云改成 R2。 这就是全部机制。
</content>
19 · 视觉升级评估 · 顶缘流光灯条 + 自定义背景
节点:看到两个爆款竖屏钢琴视频(TikTok),评估是否值得抄两个视觉特性。
结论:都做,因为 ~80% 复用现有代码,约 1 天工作量,是把视频从"能看"拉到"高级"的关键。
触发:两个参考样本
- ONE PIECE「1000回聞くやつ」(RYピアノ,赞 3,441)
- 动漫画面当背景,假名音符 ラ/ド/ミ/ソ 从上落下,底部一排键盘,键盘顶有红线。
- Rush E(minapiano,赞 227.5万)
- 暗房 + 键盘顶缘一条暖金色发光灯条,音符落下命中处爆一下辉光(bloom)。
用户提出两个想做的点:①键盘上缘发光曲线动画;②可设置背景(上传图片,YouTube 链接做模糊背景)。
评估
① 键盘顶缘发光灯条 —— 优先级最高(⭐⭐⭐)
- 是什么:键盘顶部一条常驻暖色渐变光条 + 落音符命中对应键位时局部增亮(bloom)。
- 为什么值:这是"专业感/高级感"的头号来源,Rush E 的质感 ~80% 来自它。
- 成本:低。已有雏形——
visualizer.js:290 已有"鼓点→边缘辉光",键键已用 shadowBlur。
只需:常驻渐变条 + 命中点局部 bloom。
② 自定义背景:上传图片 / YouTube 模糊背景(⭐⭐)
- 是什么:背景层除"氛围预设/原视频"外,新增"用户上传图片"与"YouTube 模糊背景"两个源。
- 已就位:
setBgVideo + drawVideoCover(cover 适配)已能放视频背景;/youtube_video 已能下载视频。 - 要补:
- 图片作为背景源(file input → drawImage cover-fit);
- 统一背景源选择器:无 / 氛围预设 / 上传图片 / YouTube;
- 模糊 + 压暗滑杆(
filter: blur() + 半透明黑遮罩)。
- YouTube 模糊背景:完全可行,复用
/youtube_video + drawVideoCover + blur。 - 估时:约半天。
③ 彩蛋:音符音名/唱名标签(顺手做,⭐)
- 参考样本①的音符上印着假名 ラドミソ(唱名)。日本市场极吃这套。
- 我们已知每个音的 midi → 加"音名(C/D/E)或唱名(ドレミ)标签"开关,近乎零成本。
决定
| 项 | 做不做 | 优先级 | 估时 | 复用 |
|---|
| ① 顶缘流光灯条 + 命中 bloom | ✅ 做 | 高 | ~0.5 天 | 边缘辉光雏形 + shadowBlur |
| ② 自定义背景(图片 / YT 模糊) | ✅ 做 | 中 | ~0.5 天 | setBgVideo + drawVideoCover + /youtube_video |
| ③ 音名/唱名标签 | ✅ 顺手 | 低 | ~1-2h | midi 已知 |
落地顺序:① → ③ → ②(先把质感拉满,再加面向日本的标签,最后做背景源)。
实现结果(已完成 ✅,2026-06-07)
三项全部落地、像素级/截图验证通过、已提交推送:
| 项 | 状态 | 实现 | 验证 |
|---|
| ① 顶缘灯条 + 命中 bloom | ✅ | piano-fx.js drawEdgeGlow:向上柔光溢出 + 亮线 + 命中处叠加爆光(衰减~20帧)。cfg.edge=theme/warm/off,所有 Look 自动获得 | 像素采样:灯条行 87% 亮像素;爆光峰值纯白(765) |
| ② 自定义背景(图片/YT模糊) | ✅ | visualizer.js bgSource(none/image/video)+setBgImage/setBgFx;通用 drawCover;超扫避免模糊黑边;YT 复用已下载的 compareVideo。新增「背景纸」面板(源+模糊+压暗)+ i18n | 像素采样 + 截图:上传图模糊压暗后透出,音符清晰可读 |
| ③ 音名/唱名标签 | ✅ | piano-fx.js cfg.label=off/abc/solfa/kana,画在音符头部;kana=日本固定唱名(ドレミ)。组合式 FX 新增「音名标签」维度 + i18n | 截图:落音符上显示 ミ/ソ/シ/ラ/ド/レ,与 One Piece 参考一致 |
实现顺序为 ①→②→③(把用户明确要的背景先于彩蛋做)。缓存版本 v68→v73。
两个待定问题 → 最终结论(已落地 ✅)
- 灯条颜色:用户希望"独立设颜色"。→ 改成
edge = 跟主题 / 自定义(取色器) / 关;cfg.edgeColor 任意色(拖取色器自动切到自定义)。原 warm 预设并入"自定义"。验证:设 #ff2020 灯条变红(像素 RGB 179/85/98 + 截图)。 - 背景=独立开关:用户明确要"一个独立开关、上传图片即设背景"。→ 背景做成与模式无关的独立层:演奏/歌词/氛围都生效(氛围=图当底+光斑浮其上),仅对比模式保留原视频(其卖点即视频)。背景面板独立于模式行,随时可设。验证:氛围模式显示上传图为底(像素 RGB 83/65/126 + 截图)。
其他待定
- ⚠️ 版权:图片/YouTube 背景 = 在音乐版权上再叠画面版权。自用无碍;对外商用按"用户自带素材 + 平台责任(DMCA)"处理,归入 docs/15 法务。
- 导出 MP4 中三特性是否一致(走同一 canvas,理论一致)——待跑一次确认。
20 · 工作室右侧面板信息架构重组(尤其 YouTube 路径)
节点:用户反馈"右侧控制面板对 YouTube 路径太复杂、有点乱"。
结论:把"出片模式 / 叠加层 / 加载器 / 模式专属设置"四类混在一起的控件拆清,并按需展开。
问题
旧的「出片模式」一个框里塞了三种不同性质的控件,看起来全是相似按钮:
| 性质 | 旧状(全挤在出片模式框) |
|---|
| ① 选模式(画面长什么样) | 演奏 / 氛围 / 歌词 / 对比 |
| ② 加载素材(动词) | 加载人声 / 加载歌词 / 加载原视频 / 贴歌词 |
| ③ 某模式专属设置(且一直显示) | 语言 / 对比布局 / 原视频音量 |
③ 那些专属设置不管在不在该模式都常驻显示 → 信息过载。刚贴完 YouTube 链接进来的人尤其懵。
决策:四类拆清 + 渐进展开
- 出片模式 = 只剩 3 个基础画面:演奏 / 氛围 / 对比。
- 歌词 = 独立叠加层(不再是模式):独立可折叠 section,一个
显示歌词开关 + 自动识别/贴LRC/语言。- 引擎层:
Visualizer.setLyricsOn(bool),渲染与 mode 解耦 → 歌词能叠在任意模式上(演奏+歌词、氛围+歌词…)。
- 对比专属控件按需展开:
#compareTools 只在选中「对比」时显示,含 加载原视频 / 对比布局 / 原视频音量。平时不占地方。 - 加载原视频改用 URL 输入框(
#cmpUrl):- YouTube 路径:自动预填已贴的链接,一点即载;
- 音频/MIDI 路径:手动粘贴链接 → 对比/背景模式不再是 YouTube 路径专属,三条路径都能用。
- 加载人声归入「声部混音」section(它本就为多轨原声服务)。混音器渲染进
#mixerBody,静态标题保留按钮。
默认只展开「出片模式」,歌词/背景纸/混音等 section 折叠 → 初进画面很干净。
渐进展开原则
控件用到才显示:对比的子控件只在对比模式出现;歌词/背景/混音折叠收起。
把"加载素材"按钮移到它服务的对象旁边(视频→对比、歌词→歌词、人声→混音)。
验证
- 启动无报错;模式 = play/ambient/compare;选对比展开
#compareTools、切走收起;歌词叠加在"演奏"模式下正常渲染(DOM 检查 + 截图)。 - 缓存版本 → v74。
待办 / 另议
- 主旋律 / 双手 / 完整 / 伴奏:发现这其实是「声部混音」里已存在的预设(多轨时出现)。用户最初说"YouTube 路径没有"——因为它在更靠下的独立 section。后续单独议:是否在 YouTube 路径更显眼地呈现,或进工作室前选。
- 三条路径(YouTube / 音频 / MIDI)是否需要各自略不同的默认折叠态。
</content>
</invoke>
21 · 歌词从哪来(中日英)· 字幕 / 强制对齐 / Whisper
节点:歌词识别怎么做?YouTube 能不能下歌词?中日文 Whisper 难。
结论:分层兜底 —— YouTube 字幕 → 强制对齐(已知文本) → Whisper → 手贴 LRC。
核心认识
- Whisper 盲听写中日文歌词最不靠谱:难点不是语言,是"唱"(拖音/转音/伴奏残留)。
small 英文勉强,中日文弱;large-v3 好但又大又慢。 - YouTube 没有"歌词下载"按钮,但有字幕(captions):作者人工字幕≈准确歌词且自带时间轴;自动字幕(ASR)也带时间轴但可能有误。
yt-dlp 能直接下。 - 强制对齐是中日文的最佳解:字你已经给它了(纯文本歌词),它只把字贴到音频时间轴 → 把最难的"识别"去掉,只留"对齐"。
分层兜底策略
| 优先 | 来源 | 时间轴 | 适用 | 状态 |
|---|
| 1️⃣ | YouTube 字幕(yt-dlp,优先人工) | ✅ 自带 | YouTube 路径、视频有字幕 | ✅ 已做 |
| 2️⃣ | 强制对齐(纯文本歌词 + 人声音频) | ✅ 算出 | 中日英、有歌词文本无时间 | ⏸ 暂缓(需装 aeneas/espeak,用户决定先不做) |
| 3️⃣ | LRCLIB / 网易云(按曲名取 LRC) | ✅ | 流行歌 | 备选 |
| 4️⃣ | Whisper 识别人声(Demucs 人声轨) | ✅ 逐词 | 任何音频、兜底 | ✅ 已有 |
| 5️⃣ | 手贴 LRC | ✅ | 任何、最终兜底 | ✅ 已有 |
路径差异:
- YouTube 路径:① 字幕 → ② 对齐(贴词) → ④ Whisper → ⑤ 手贴
- 音频/MIDI 路径:无视频,②/③ → ④ → ⑤
① YouTube 字幕(已实现 ✅,v76)
youtube_service.fetch_subtitles():extract_info 列出字幕轨 → 人工优先于自动,语言优先 zh/ja/en → 下选中的 vtt。_parse_vtt():清掉内联计时标签 <...>,对自动字幕的"滚动重复"去重 → [{start,end,text}]。- 端点
/youtube_lyrics → {lines, lang, source};前端「🎬 取 YouTube 字幕」按钮 → 喂现有歌词叠加层。 - 验证:真实视频取到人工英文字幕 6 行(文字+时间正确),渲染为卡拉OK叠加。
② 强制对齐(⏸ 暂缓 —— 用户决定先不做,方案存档)
决策:Phase 1 的 YouTube 字幕 + 手贴 LRC + Whisper 兜底已覆盖大量场景;
强制对齐需装 aeneas/espeak(macOS 安装偶有摩擦),暂不做。等"有歌词文本但没时间"成强需求再启用。下方方案备查。
- 输入:纯文本歌词(用户贴/Genius/LRCLIB 无时间版)+ 人声音频(Demucs 人声轨最准)。
- 工具选型:
- aeneas ✅ 推荐 —— 专为"文本按行对齐音频"设计,正合 LRC 卡拉OK逐行;支持中/日/英(espeak);轻量、无需 GPU。
- whisperX —— 逐词、英文极好但中日弱、要 torch。逐词非必需,不选。
- 代价:装系统依赖
espeak + pip install aeneas(待确认后再装)。 - 产物:行级 LRC → 现有
setLyricLines。
没歌词怎么办
- 纯器乐/无人声 → Whisper/对齐为空 → 不报错,提示"这首没有可识别歌词",歌词层留空。(体验可再打磨)
⚠️ 版权
歌词受版权。本地/自用无碍;对外商用抓字幕/LRCLIB/网易云属灰色,正经做要 Musixmatch/LyricFind 授权。归 docs/15 法务。
</content>
22 · 阶段 2 · 后端 AI 上 Modal(部署手册)
目标:把后端(Pop2Piano/Demucs/Whisper/yt-dlp/ffmpeg)发布到 Modal 云 GPU,
让线上 dev.pianoreel.com 的 MP3 上传 / YouTube / 歌词真正能用。
部署文件:prototype/backend/modal_app.py。
1. 一次性准备(你做)
cd ~/Documents/github/pianoreel/prototype/backend
.venv/bin/pip install modal
.venv/bin/python -m modal setup # 弹浏览器授权,把账号和本机连起来
- Modal 账号:modal.com → GitHub 登录注册,每月 $30 免费额度。
2. 部署(你做,我盯)
cd ~/Documents/github/pianoreel/prototype/backend
.venv/bin/modal deploy modal_app.py
- 首次会 build 镜像(装 ffmpeg/fluidsynth/lilypond + torch 等,几分钟)。
- 成功后给你一个地址,形如
https://<账号>--pianoreel-web.modal.run。把这个地址发我。
3. 让前端指向它(我做)
把上面的地址填进 prototype/web/js/core/config.js 的 PROD_BACKEND,push → Cloudflare 自动重部署 → 线上 MP3/YouTube/歌词就活了。
4. modal_app.py 里做了什么
- 镜像:debian + apt(ffmpeg/fluidsynth/lilypond/fluid-soundfont-gm) + pip(torch/transformers/demucs/faster-whisper/librosa/music21/yt-dlp…)。
- GPU:T4(够用、便宜);
scaledown_window=180 没人用 3 分钟后缩到 0(省钱)。 - 模型缓存:HF/torch/Whisper/Demucs 缓存到持久卷
pianoreel-cache,冷启动不重下。 - 音色:用系统 FluidR3_GM 顶替
backend/soundfonts/piano.sf2(原 141MB 不进 git)。 - CORS:
ALLOWED_ORIGINS 只放行 dev/主域。 - 产物:后端自己
mount /samples(StaticFiles)serve,前端跨源用绝对地址取。
5. 已知待办 / 首次部署会一起调
- 前端取 samples 用绝对地址:现在
audioUrl/videoUrl 是相对 samples/..。本地同源没问题;
线上要前端把它解析到 PROD_BACKEND。等拿到 Modal 地址后我加一个 mediaUrl() 解析(要联调,所以放部署时做)。
- 产物持久化:samples 现在在容器内,冷启动会重生成。要长期稳定 → 接 R2(阶段 3,docs/18 §5)。
- 首请求慢:冷启动 + 下模型 ~1-2GB;缓存后很快。
- basic-pitch 旧路径:Modal 镜像未装(省体积),主流程走 Pop2Piano,不受影响。
- 成本:T4 按秒,单首几日元;$30/月免费额度基本够测。见 docs/18。
5.5 首次部署踩的坑(已解,记录备查)
cannot mount volume on non-empty path /cache → 崩溃循环。
原因:把 HF_HOME/TORCH_HOME/XDG_CACHE_HOME=/cache/.. 写成镜像 ENV,build 期就往 /cache 写缓存,
导致挂卷时 /cache 非空。修:这些缓存路径改在 web() 运行时(卷挂好后)再 os.environ 设置。
/arrange 500:'NoneType' object is not callable(proc=None,秒崩)。
原因:Pop2Piano 处理器依赖 essentia;① 镜像漏装 essentia;② 装了但 **essentia 2.1b6 用 numpy 1.x ABI 编译,
遇 numpy 2.x → import essentia 崩 → is_essentia_available()=False → 处理器构建成 None**。
修:pip_install("essentia","resampy") + 主层钉 numpy<2。
诊断技巧:在 web() 里临时加 /debug_deps 端点,curl 秒看依赖(免冷启诊断容器)。
- YouTube 被限流(
/youtube* 报“要求验证”)。
原因:Modal 数据中心 IP 被 YouTube 封(docs/13/18 早有预判)。MP3 上传路径不受影响。
待解:cookies / 住宅代理 / 桌面端下载的混合架构(docs/18 §5.5)——单独处理。
当前状态(阶段 2)
| 能力 | 云端 | 说明 |
|---|
/health · CORS | ✅ | 放行 dev/主域 |
| MP3 上传 → Pop2Piano 编曲 | ✅ | T4 GPU,首次载模型~28s,之后更快 |
| 分轨 / 谱子 / 高音质 / 转码 | 🔜 | 同镜像应可用,待逐个验证 |
| YouTube 下载(音频/视频/字幕) | ❌ | 数据中心 IP 被封,待 cookies/代理/混合 |
6. 排错
- build 失败 → 看是哪个 pip 包;版本冲突就放宽 pin。
- 跑起来但报错 →
.venv/bin/modal app logs pianoreel 看日志。 - 显存不够(OOM)→ 把
gpu="T4" 改 "A10G" 或 "L4"。
</content>
23 · 双轨产品策略:网页版(干净)+ 桌面 App(全功能)
节点:YouTube 云端下载被数据中心 IP 封 + 下载本身违反 ToS/版权(灰色)。
决策:不在网页端做 YouTube 下载;网页保持"干净",YouTube 全部交给"桌面 App"在用户本机完成。
形成网上 + 线下两条路。
决策
| 维度 | 🌐 网页版 pianoreel.com | 💻 桌面 App(本地) |
|---|
| 输入 | 只接受上传音频(MP3/WAV/M4A/MIDI) | YouTube 下载 + 上传音频 + 解析(全部) |
| 后端 | Modal 云 GPU(已上线,MP3→钢琴 已通) | 本地后端(用户机器上跑,现有 make dev 那套) |
| 网络/IP | 不碰 YouTube → 不会被封 | 用户住宅 IP → YouTube 能正常下 |
| 定位 | 干净、安全、合规友好、门槛低、即开即用 | 全功能、强大,面向重度/创作者 |
| 版权负担 | 我们的服务器不下载任何 YouTube ✅ | 下载发生在用户自己机器,责任随之转移 |
一句话:把 YouTube 下载的 IP 封锁 + 版权/ToS 负担,从"我们的云服务器"转移到"用户自己的电脑"。网页端保持干净低风险,桌面端保留全部能力。
为什么这样分
- 网页端做 YouTube = 三重麻烦:数据中心 IP 被封(技术)、违反 ToS(合规)、平台 DMCA 责任落到我们(法律)。直接不做,最省心。
- 桌面端做 YouTube = 自然合理:住宅 IP 不被封;下载行为在用户本机、由用户发起,和"用户自己用 yt-dlp"性质一样,平台责任不在我们。
- 网页端的「干净」反而是卖点:纯"上传你的音频 → 出钢琴视频",无版权下载嫌疑,适合正式商用/收费。
两版怎么共用
- 同一套前端(
prototype/web,纯静态):网页版连 Modal;桌面版连本地后端。靠 config.js 按环境切 BACKEND_URL(已具备)。 - 同一套后端代码(
prototype/backend):网页版部署到 Modal(modal_app.py);桌面版把它作为本地服务打包进去。 - 差异仅在"后端在哪 + 是否启用 YouTube",不分叉代码主体。
现状
- ✅ 网页版:前端上线(Cloudflare)+ 后端上云(Modal)+ MP3→钢琴 已跑通。YouTube 入口线上不可用(符合本策略)。
- ⏳ 桌面 App:未开始。现有"本地
make dev"已经是它的内核(YouTube/上传/解析全能用),只差打包成 App。
待定(桌面 App 起步时再定)
- 打包方案:Tauri(轻)vs Electron + Python sidecar(PyInstaller 打包后端)。见 docs/15 §2。
- 重依赖随包:torch/模型/ffmpeg/fluidsynth/lilypond —— 体积大(1–3GB),首启下载 vs 打进包。
- 前端去 CDN:
@tonejs/midi、soundfont-player 本地化(离线可用)。 - 两版定价/差异:网页免费/订阅 vs 桌面一次性/Pro?YouTube 作为桌面专属卖点。
- 自动更新 + macOS 代码签名/公证。
关联文档
- 打包与部署:docs/15 · 分发与收费:docs/16 · 运作机制与成本(含混合架构):docs/18
- 后端上 Modal:docs/22 · YouTube 导入与 IP 问题:docs/13
</content>
24 · 统一账号 + 付费(网页 & 桌面共用)
节点:双轨(docs/23)下,账号怎么处理?
决策:统一账号 —— 一个账号、一份订阅,网页版和桌面 App 通用。
决策
- 一个账号系统,网页和桌面都登同一个。
- 一份订阅:任意一端用 Stripe 订阅一次 → 账号变 Pro → 两端都解锁。
- 标准做法(类比 1Password / Spotify:一个账号,多端通用)。
架构
┌──────── 云端:唯一的账号 / 付费中心 ────────┐
│ Auth(登录) + 订阅状态(Stripe) │
│ Cloudflare Workers + D1 + Stripe │
└────▲──────────────────────────▲───────────┘
│ 登录 / 查授权 │ 登录 / 查授权
🌐 网页版 💻 桌面 App
(ML 在 Modal) (ML/YouTube 在本地)
- 登录:邮箱 / Google 登录,两端连同一套 Auth。
- 付费:Stripe 订阅绑定到账号;账号上有个"套餐/到期"字段。
- 授权检查:两个客户端登录后向云端查"是不是 Pro / 能用哪些功能",据此解锁。
- 桌面 App 仍需联网做两件事:① 登录 ② 查授权。ML/YouTube 在本地跑,但账号/授权在云端(可留离线宽限期,断网也能用几天)。
关键点:功能由"账号套餐"决定,不由客户端决定
- 后端给账号一个
plan(free / pro / …)字段。 - 网页和桌面都读同一个
plan → 同一个账号在哪端都是同样的权限。 - YouTube 是否可用:技术上只有桌面能下(IP),商业上是否要 Pro 由
plan + 端类型共同决定(见下方定价)。
待定
- 定价 A/B(未定):
- A. 一份 Pro 通吃:订阅后网页+桌面都解锁(简单、好理解)。
- B. 桌面更高一档:YouTube 下载当桌面专属卖点,桌面 Pro 单独/更贵(用户之前倾向)。
- 不管 A/B,账号都统一,只是 plan 的解锁范围不同。
- 登录方式:邮箱+密码 / Google / 都要?(Google 最省心)
- 桌面离线宽限:断网允许用多久(如 7 天)后必须重新联网验证。
- 设备数限制:一个账号几台设备。
- 免费额度:免费用户每月几首 / 带水印;Pro 去水印 + 配额更高。
现状 / 依赖
- ⏳ 未开始(属商业化 / 阶段 4)。
- 技术栈已规划:Cloudflare Workers + D1(账号/订阅)+ Stripe(收款)+ R2(作品存储)。见 docs/16、docs/17 §4、docs/18。
- 顺序建议:先把网页版打磨稳 → 桌面 App 打包 → 再上账号/付费(有人用了再收费)。
</content>
📒 项目文档 Playbook —— 把成长过程写下来(可复制到任何项目)
这是一套项目无关的文档组织与维护方法:一个项目从 0 慢慢长大,
每个目标、决策、功能、取舍都留一篇记录,形成可追溯的成长史。
直接把本文 + tools/build-docs.js 复制到新项目即可起步。
一、核心理念
- 写过程,不只写结果 —— 不是"做完再补文档",而是边做边记:每做一个大功能 / 大决策,就留一篇。
- 文档会长大 —— 随项目持续更新,不是写完就扔。
- 一个权威源 —— 永远有一篇"当前状态"作为唯一真相,别处过时了以它为准。
- 可浏览 —— 把 markdown 编译成一个离线 HTML 文档站,自己/协作者/非技术的人都能一页翻完。
二、目录结构
项目根/
├── README.md 入口:本地启动、服务/端口、快速上手
├── ARCHITECTURE.md 架构地图:代码在哪、数据怎么流、怎么加东西
├── docs/
│ ├── 00-status.md ⭐ 唯一权威状态(目标 / 进度 / 最终形态)—— 第一个看的
│ ├── 01-xxx.md 按主题/决策/功能编号递增
│ ├── 02-xxx.md (每个里程碑或大决策一篇)
│ ├── … 13-youtube · 14-vision · 16-monetization · 18-architecture …
│ └── DOC-PLAYBOOK.md 本文(方法论本身)
├── tools/build-docs.js 把 docs/*.md → 单文件 docs.html(无依赖、离线)
└── (生成)web/docs.html 可浏览的文档站
三类文档分工:
00-status.md —— 目标 / 当前进度(✅做了 · 🔄在做 · ⏳没做)/ 最终形态 / 关键指标。随时更新。- 编号 docs(01..NN) —— 每篇一个主题:功能设计、技术决策、方案发散、成本、上线…。只增不删。
ARCHITECTURE.md —— 给改代码的人:每个文件干什么、数据怎么流、"怎么加一个 X"。
三、工具:build-docs.js
把 docs/*.md 编译成一个自包含、离线可开的 docs.html(左侧导航 + markdown 渲染,支持标题/表格/代码/引用/列表)。
- 在脚本顶部
FILES 数组里登记每篇:{ file, title, icon },顺序即导航顺序。 - 跑
node tools/build-docs.js(或 make docs)重新生成。 - 无第三方依赖,纯 Node 一个文件,可直接复制到任何项目。
四、维护纪律(养成习惯)
- 每个大决策 / 大功能 → 一篇编号 doc(例:13 YouTube 导入、14 形态发散、16 分发收费、17 上线、18 机制与成本)。
00-status.md 随时更新 —— 完成/在做/没做、关键指标。它是"现在到哪了"的唯一答案。- 改了任何 doc →
make docs 重建 docs.html,和 doc 一起提交。 - 一篇一提交,commit message 写清这篇讲什么(将来翻 git log 就是项目编年史)。
- 旧文档过时不删 —— 顶部加一句"以
00-status.md 为准"的横幅,保留历史比删掉更有价值。 - 决策类文档留「待定问题」小节 —— 持续填,把纠结写下来避免反复。
- 聊出来的重要结论及时落档 —— 讨论清楚一个取舍,就顺手写进对应 doc(别只留在聊天里)。
五、在新项目里起步(5 步)
- 建
docs/,先写 docs/00-status.md(目标 / 进度 / 最终形态 三段)。 - 复制
tools/build-docs.js,改 FILES 列表为你的文档。 - 加一个
make docs(或 npm script)跑 build-docs。 - 之后每个里程碑/决策写一篇,更新
00-status,make docs 重建,提交。 - (可选)写
ARCHITECTURE.md 当代码地图;前端项目加 make bump 管缓存版本。
六、为什么这套有用
- 给几周后的自己:还记得"当初为什么这么决定",不用从头想。
- 给协作/外包:新人看
00-status + ARCHITECTURE 就能上手。 - 给决策:把取舍写下来,避免反复纠结、来回改。
- 给非技术的自己/投资人:
docs.html 一页翻完全局。 - 给项目本身:git 历史 + 编号文档 = 一部可追溯的成长史。
一句话:**每做一件大事,就留一篇;永远有一篇 00-status 说清现在到哪了;
把 markdown 编成可浏览的 docs.html。** 这就是全部方法。
</content>