# midi **Repository Path**: qfmx/midi ## Basic Information - **Project Name**: midi - **Description**: 基于 AI 模型的音频转游戏钢琴谱 Web 应用。上传音频文件,自动转录为 MIDI 并转换为「创造与魔法」游戏高音钢琴的 `15#` 代码格式。支持长曲分段,每段独立输出为可复制的 `15#` 代码块。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-07 - **Last Updated**: 2026-05-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 音频转游戏钢琴谱工具 基于 AI 模型的音频转游戏钢琴谱 Web 应用。上传音频文件,自动转录为 MIDI 并转换为「创造与魔法」游戏高音钢琴的 `15#` 代码格式。支持长曲分段,每段独立输出为可复制的 `15#` 代码块。 ## 转换流程 ``` 音频文件 (MP3/WAV/OGG/FLAC/M4A) → Spotify basic-pitch AI 模型转录为 MIDI → mido 解析 MIDI 事件 → 映射为游戏 15 键位代码 → 按时长分段,每段输出 15# {{键位,时间},...} 格式 ``` ## 功能特性 - **AI 音频转录** — 基于 Spotify basic-pitch ONNX 模型,无需 TensorFlow - **全音域映射** — 自动八度移位 + 黑键取最近白键,支持任意 MIDI 音高输入 - **智能分段** — 长曲自动按设定时长分段,每段独立生成 `15#` 代码 - **钢琴卷帘** — 可视化音符分布,分段分隔线一目了然 - **一键复制** — 单段复制 / 全部复制,带复制成功反馈 ## 运行截图 ![应用界面](docs/Snipaste_2026-05-07_18-03-21.png) ## 技术栈 | 层级 | 技术 | 版本 | |------|------|------| | 前端 | Vue 3 (Composition API) | ^3.5 | | 前端语言 | TypeScript | ^5.7 | | 构建工具 | Vite | ^6.2 | | 前端包管理 | pnpm | - | | 后端 | FastAPI | >=0.115 | | ASGI 服务器 | Uvicorn | >=0.34 | | Python 包管理 | uv | - | | Python 版本 | Python | >=3.11 | | AI 模型 | Spotify basic-pitch (ONNX) | >=0.4 | | MIDI 处理 | mido | >=1.3 | ## 项目结构 ``` webapp/ ├── backend/ # FastAPI 后端 │ ├── main.py # API 入口 │ ├── audio_to_midi.py # 音频 → MIDI 转换 │ ├── midi_to_cm_piano.py # MIDI → 游戏钢琴谱代码(含分段) │ ├── pyproject.toml # Python 依赖配置 │ ├── test_script.py # 测试脚本 │ └── uploads/ # 上传文件目录 └── frontend/ # Vue 3 前端 ├── src/ │ ├── App.vue # 主布局 │ └── components/ │ ├── FileUpload.vue # 文件上传(拖拽/点击) │ ├── ParameterPanel.vue # 参数调节(频率 + 分段时长) │ └── ResultView.vue # 结果展示(钢琴卷帘 + 分段代码 + 复制) ├── package.json └── vite.config.ts ``` ## 快速开始 ### 环境要求 - Python >= 3.11 - Node.js >= 18 - pnpm - [uv](https://docs.astral.sh/uv/) (Python 包管理器) ### 启动后端 ```bash cd webapp/backend uv sync uv run python main.py ``` 后端运行在 http://127.0.0.1:8000 ### 启动前端 ```bash cd webapp/frontend pnpm install pnpm dev ``` 前端运行在 http://localhost:5173,API 请求自动代理到后端。 ## API 接口 ### 上传并转换 ``` POST /api/convert Content-Type: multipart/form-data 参数: file: UploadFile # 音频文件 minimum_frequency: float # 最低频率 (默认 80) maximum_frequency: float # 最高频率 (默认 2000) segment_duration: int # 分段时长/秒 (默认 60, 范围 10~300) 返回: { "task_id": "a1b2c3d4", "cm_code": "15# {{1,0},{3,500},...}", "segments": [ { "index": 1, "start_ms": 0, "end_ms": 60000, "note_count": 42, "cm_code": "15# {{1,0},{3,500},...}" }, { "index": 2, "start_ms": 60000, "end_ms": 120000, "note_count": 35, "cm_code": "15# {{5,0},{2,300},...}" } ], "note_count": 77, "pitch_range": "60~84", "notes": [{"pitch": 60, "time_ms": 0}, ...] } ``` > `cm_code` 为完整合并代码;`segments` 为分段代码数组,每段时间相对于该段起始。仅 1 段时前端显示单段模式。 ### 下载 MIDI ``` GET /api/download/{task_id} 返回:MIDI 文件 ``` ## 键位映射 游戏高音钢琴 15 个键位对应 C 大调自然音阶(白键): | 键位 | 音名 | MIDI 音高 | |------|------|-----------| | 1 | C4 | 60 | | 2 | D4 | 62 | | 3 | E4 | 64 | | 4 | F4 | 65 | | 5 | G4 | 67 | | 6 | A4 | 69 | | 7 | B4 | 71 | | 8 | C5 | 72 | | 9 | D5 | 74 | | 10 | E5 | 76 | | 11 | F5 | 77 | | 12 | G5 | 79 | | 13 | A5 | 81 | | 14 | B5 | 83 | | 15 | C6 | 84 | 超出 C4~C6 范围的音符会自动按八度移位,黑键(升半音)会自动取最近的低半音白键。映射逻辑: 1. **八度移位** — 音高 < 60 则 +12,> 84 则 -12,直到落入 C4~C6 范围 2. **黑键→白键** — C#→C, D#→D, F#→F, G#→G, A#→A(向下取最近白键) ## 注意事项 - 后端使用 `basic-pitch` 的 ONNX 推理路径,不依赖 TensorFlow,通过 `uv` override 排除了 tensorflow 相关包 - `setuptools<78` 是 `resampy`(basic-pitch 依赖)的 `pkg_resources` 兼容性要求 - 转换精度受 AI 模型限制,复杂多声部音乐可能需要调整频率参数 - 分段时长默认 60 秒,可在前端参数面板调节(10~300 秒)