亡命迪斯科MDO自定义歌曲包制作全流程:从BPM测定到谱面打包
发布时间:2026/9/7 2:08:36来源:尧图网络
在音乐游戏社区里自定义歌曲一直是延长游戏生命力的关键玩法。《亡命迪斯科》的玩家同样不满足于官方曲库而是希望把喜欢的歌曲做成谱面放进游戏里体验。MDO 就是这类自定义歌曲资源包里最常出现的格式之一。这次以日本歌手広瀬香美的《Groovy!》为示例完整走一遍从音频准备、BPM 测定、谱面编写到 MDO 打包、导入游戏、验证和排错的流程。整篇文章的目标是让读者最终能做出一个自己可以玩、也能分享给其他人的自定义歌曲包并且知道出问题时该从哪一层开始检查。MDO 看似只是一个文件实际背后是音频、谱面、元数据和封面的组合规范。很多人第一次做自定义歌曲卡住的不是谱面难写而是文件结构、命名、路径和编码这些小细节。只要把这些底层规则理解清楚后面的制作过程会顺很多。1. 先理解 MDO 自定义歌曲包的工作方式1.1 MDO 是什么解决了什么问题MDO 是《亡命迪斯科》社区中一类自定义歌曲资源包的统称。可以把 MDO 理解成“一首歌的完整封包”它把游戏读取这首歌所需要的音频、谱面、封面、标题、作者、BPM、偏移量等信息集中放在一个文件里。玩家只需要把 MDO 文件放到指定目录游戏启动时就会扫描并把它加入曲库。这个设计解决的问题很直接官方曲目通常以加密或专用的数据格式存放在游戏包里玩家无法直接改文件来增加歌曲。MDO 相当于一个自定义歌曲的统一交换单位类似其他音乐游戏中的.osz、.bsaber等格式。社区只要约定好内部文件结构和字段玩家就能用压缩工具自己组装一个可识别的歌曲包。需要说明的是MDO 的具体实现可能因为游戏版本和社区补丁不同而存在差异。本文介绍的是社区中较通用的做法把 MDO 当作一个 zip 容器里面放清单文件、音频、封面和谱面数据。落地到具体版本前要先确认自己的游戏版本支持哪种自定义歌曲加载方式。1.2 MDO 包内通常包含哪些内容一份典型的 MDO 资源包内部结构大致如下Groovy.mdo ├── manifest.json ├── audio.ogg ├── preview.ogg ├── cover.png └── chart.json各部分的作用如下manifest.json整个包的入口描述记录歌曲标题、艺术家、谱面作者、BPM、偏移量、音频文件路径、封面路径等。audio.ogg完整游玩时播放的音频文件。推荐 OGG Vorbis 格式体积和兼容性更均衡。preview.ogg歌曲预览片段用于曲库列表中的试听可以比完整音频短很多。cover.png歌曲封面在曲库选择和游戏内显示。chart.json谱面数据包含音符时间、轨道、类型、力度等关键信息。有些版本还会包含 meta.xml、difficulty 字段或者多难度谱面目录。即便内部结构稍有不同核心思路一致用清单文件告诉游戏“这首歌是谁的、用什么音频、读哪份谱面”用实际资源文件承载内容。1.3 制作前需要确认的版本与路径制作 MDO 前先回答下面几个问题当前游戏版本是否已经支持自定义歌曲加载。自定义歌曲目录是游戏根目录下的CustomSongs还是用户数据目录下的某个文件夹。游戏是否要求先把 MDO 解压成文件夹还是支持直接读取压缩包。游戏启动扫描自定义歌曲的时机是什么是每次启动都扫描还是需要手动刷新。游戏内是否有开发者模式或自定义歌曲开关。这些信息看起来基础却是绝大多数导入失败的原因。比如把 MDO 放到了游戏根目录但游戏只扫描用户目录又比如游戏需要先解压再导入直接把.mdo放进去就不会显示。获取这些信息的方式很简单查看游戏设置里有没有“自定义歌曲”或“音乐包”入口或者查看游戏日志启动时输出的路径。注意不要在游戏运行过程中替换、删除或写入自定义歌曲目录。Windows 下压缩包可能被文件占用macOS 和 Linux 下也可能因为缓存导致扫描结果异常。正确做法是退出游戏后再操作文件重启游戏再验证。2. 环境准备音频处理、BPM 测定与谱面工具2.1 推荐工具清单制作 MDO 不需要重量级开发环境但要准备几类工具。下面表格列出用途和选择理由。工具用途说明ffmpeg音频转码、裁剪、格式检查命令行工具适合批量处理输出格式稳定AudacityBPM 测定、裁剪、响度调整图形界面方便人耳听辨和查看波形文本编辑器编辑 manifest.json 和 chart.json推荐 VS Code能高亮 JSON 并对格式错误给出提示压缩工具打包 MDOWindows 自带压缩、macOS 自带归档工具、命令行 zip 均可Python批量计算音符时间、校验文件结构可选适合谱面较复杂或需要批量生成时使用这些工具中ffmpeg 和 Audacity 是核心。ffmpeg 负责把 flac、mp3、wav 转成游戏需要的 ogg并且可以裁剪出预览片段。Audacity 则适合在转码前完成 BPM 测定和人耳校准。2.2 音频文件规范化格式、码率、响度自定义歌曲制作的第一步不是写谱面而是先把音频处理成游戏能稳定读取的文件。如果原始歌曲是 flac 或 mp3建议统一转成 OGG Vorbis 格式。原因有两点一是 OGG 开源且体积小多数音游加载器对它支持较好二是用统一格式可以避免某些游戏在切换歌曲时因为解码器兼容问题崩溃。ffmpeg 转码命令示例ffmpeg -i Groovy.flac -ac 2 -ar 44100 -b:a 192k audio.ogg参数含义-ac 2双声道。除非谱面机制明确要求单声道否则保留双声道能获得更好的听感。-ar 44100采样率 44100HzCD 音质标准也是多数引擎的默认音频处理频率。-b:a 192k音频码率 192kbps。语言类或低动态歌曲可以降到 128k电子乐和编曲密集的歌曲可以升到 256k。audio.ogg输出文件名需要与 manifest 中填写的一致。歌曲响度也需要注意。音游谱面通常按照音乐节拍设计响度过低会导致玩家听不清重音响度过高则可能削波。Audacity 里可以查看波形是否触顶尽量把峰值控制在 -3dB 到 -6dB 之间。这个区间既保证了听感也留了动态余量。如果想在 Audacity 中调整响度可以选中全部音频后使用“效果 响度归一化”目标响度可以设置在 -16 LUFS 左右。这属于前置处理能减少不同歌曲之间的音量跳变但不同游戏的自定义歌曲音量为独立控制实际标准要以游戏内听感为准。2.3 BPM 测定与节拍网格分析BPM 是音乐游戏谱面的基石。BPM 不准确后续所有时间点都会偏差谱面会出现“音符越打越飘”的感觉。BPM 测定有两种常见方式。一种是直接用 Audacity 的节拍跟踪器导入音频后选择“分析 节拍跟踪器”软件会根据波形能量估算一个 BPM。这个值只能作为参考因为音频里的鼓点密度、强弱拍变化都可能影响估算结果。另一种方式是自己数。在 Audacity 里选择一段 8 拍或 16 拍的区间数出对应秒数然后用公式计算BPM 60 × 拍数 / 秒数例如 5 秒内数出 10 拍那么 BPM 60 × 10 / 5 120。这个办法看起来原始但对流行歌曲非常实用因为流行歌曲的鼓点通常是稳定的四分音符或八分音符。需要注意很多歌曲并不是从头到尾一个 BPM 从不变。有的歌曲会有变速段、rubato 段或桥段。对于自定义谱面最简单的方式是先以主歌或副歌的稳定 BPM 为准如果变速明显则需要在谱面文件里支持多段 BPM 分段。具体要看自己的游戏谱面格式是否支持不要默认所有格式都支持。3. 从歌曲音频到谱面数据3.1 先确定前奏和第一个强拍的偏移量BPM 确定了歌曲的节奏速度但旋律开始的位置同样重要。音游谱面里offset 表示谱面时间原点与音频时间原点之间的差值通常定义为“音频播放开始后第一个强拍实际出现的时刻”。在音乐编辑软件里把音频放到 0 秒处播放时注意第一个重拍记录它出现的秒数。这个秒数就是初版 offset。举一个计算示例如果第一个强拍出现在 0.14 秒那么谱面里的 offset 就应该设置为 0.14。这里单位是秒还是毫秒取决于谱面文件定义。有的游戏用毫秒有的用秒进入 chart.json 前必须确认。3.2 谱面文件的数据结构一个标准的 chart.json 示例{ title: Groovy!, artist: 広瀬香美, difficulty: Normal, level: 4, bpm: 128.0, offset: 0.14, audio: audio.ogg, cover: cover.png, notes: [ { time: 0.61, track: 0, type: tap }, { time: 0.61, track: 1, type: hold, end: 1.08 } ] }这里每个字段的含义title歌曲标题会显示在曲库中。artist艺术家名字可以考虑改为罗马音或英文写法避免部分游戏字体不支持日文。difficulty难度名可以叫 Easy、Normal、Hard也可以自定义。level难度等级数字用于曲库排序。bpm歌曲每分钟拍数。offset第一个强拍出现的时间偏移单位由游戏决定。notes音符数组每个音符包含 time、track、type 等信息。type 为 tap 表示单点hold 表示长按结尾时间用 end 表示。不同游戏对字段命名差异很大。有的用beat表示节拍序号而不是秒有的用lane表示轨道有的不叫hold而叫long。所以这份示例只能作为结构参考不能直接套到所有版本的《亡命迪斯科》上。正确做法是先导出一份官方曲目或他人制作能正常运行的 MDO观察它内部实际的 JSON 字段再照着字段写自己的谱面。3.3 用脚本计算音符时间点手动计算每个音符的秒数容易出错尤其当 BPM 不是整数时。先用公式理解每拍时长 60 / BPM 第 N 拍的秒数 offset N × 每拍时长假设 BPM 为 128offset 为 0.14每拍时长 60 / 128 0.46875 秒 第 1 拍 0.14 1 × 0.46875 0.60875 秒 第 2 拍 0.14 2 × 0.46875 1.0775 秒如果谱面需要精确到毫秒可以写成 Python 脚本bpm 128.0 offset 0.14 beat_duration 60.0 / bpm note_beats [0, 1, 2, 3, 4] # 每个元素代表第几拍 for beat in note_beats: time_in_seconds offset beat * beat_duration print(fbeat {beat}: {time_in_seconds:.3f}s)输出beat 0: 0.140s beat 1: 0.609s beat 2: 1.078s beat 3: 1.546s beat 4: 2.015s这个脚本只是为了说明计算思路。实际谱面中音符不会只落在整数拍上还会有八分音符、十六分音符和停顿。把这些细分值乘上 0.5、0.25 即可。这里最容易踩的坑是“把第 0 拍当成第 1 拍”。如果 offset 表示的是第一拍出现时间那么音符数组中的第 0 拍就是 offset 本身第 1 拍是 offset 1 拍。混淆这个下标会让整份谱面提前或延后一拍。4. 制作一个最小可导入的 MDO 文件4.1 文件命名和目录结构制作 MDO 前先把所有资源放在同一个工作目录里Groovy/ ├── manifest.json ├── audio.ogg ├── preview.ogg ├── cover.png └── chart.json命名约定建议全部使用小写字母、数字、下划线尽量不出现空格和中文。音频文件名与 manifest 中的audio字段完全一致。封面文件名与 manifest 中的cover字段完全一致。遇到日文曲名在 manifest 里保留原文但外层目录名使用英文或拼音。外层目录名可以用Groovy_Kohmi避免带日文的目录在部分操作系统或游戏引擎中产生编码问题。封面推荐 PNG 格式尺寸可以准备一个 512×512 或 1024×1024 的方形图片不要用超大分辨率会增加加载时间。manifest.json 的最小写法{ title: Groovy!, artist: Kohmi Hirose, charter: your_nickname, difficulty: Normal, level: 4, bpm: 128.0, offset: 0.14, audio: audio.ogg, preview: preview.ogg, cover: cover.png, chart: chart.json }注意两点第一artist 字段这里使用罗马音可以避免日文汉字在缺字库环境下显示成方块第二BPM、offset 等数值字段不要写成字符串否则 JSON 解析可能会失败。4.2 打包命令与扩展名处理MDO 本质上是一个 zip 容器。打包的目的是把整个Groovy/目录内所有文件打到一个压缩包中然后把扩展名从.zip改成.mdo。macOS 或 Linux 下可以用 zip 命令cd /path/to/working_dir zip -r Groovy.mdo Groovy/注意这里打包的是Groovy/目录本身不是进入目录后打包所有文件。这样当游戏解压 MDO 时会看到一个Groovy/顶层目录。Windows 下可以用 PowerShellcd C:\your\path\working_dir Compress-Archive -Path .\Groovy\* -DestinationPath .\Groovy.zip Rename-Item -Path .\Groovy.zip -NewName Groovy.mdo执行完后得到Groovy/ └── Groovy.mdo将Groovy.mdo复制到游戏自定义歌曲目录。如果游戏要求直接读取文件夹而不是压缩包就不要做成 MDO 文件而是保留Groovy/文件夹并把整个文件夹放进自定义歌曲目录。这种差异需要在制作前确认。4.3 导入前的最小自检导入游戏前先做以下几项检查能避免大部分“游戏里不显示”的问题manifest.json 是否是合法的 JSON可以用文本编辑器直接打开观察是否有红色波浪线。audio、preview、cover、chart字段指向的文件是否真实存在于目录中。audio.ogg 与 preview.ogg 是否能正常播放。cover.png 是否能正常打开。chart.json 里的bpm是否大于 0offset是否在合理范围音符是否不会早于 0 秒或晚于音频总时长。可以写一个简单的 Python 校验脚本import json import os base_dir Groovy manifest json.load(open(f{base_dir}/manifest.json, encodingutf-8)) chart json.load(open(f{base_dir}/{manifest[chart]}, encodingutf-8)) for key in [audio, preview, cover, chart]: path f{base_dir}/{manifest[key]} if not os.path.exists(path): print(fmissing: {path}) if manifest[bpm] 0: print(invalid bpm) for note in chart[notes]: if note[time] 0: print(fnote time 0: {note})这个脚本不复杂但能在导入游戏前把最基本的结构问题暴露出来。日志排查是最后的兜底前置自检始终更省时间。5. 把 MDO 导入游戏并验证5.1 放置到自定义歌曲目录把生成好的Groovy.mdo放到自定义歌曲目录。常见的目录可能是游戏安装目录/CustomSongs/ 游戏安装目录/Songs/Custom/ 用户文档目录/My Games/DeathDisco/CustomSongs/不同版本目录名不同必须提前确认。不能只看压缩包放进去就认为成功还要让游戏完成一次目录扫描。5.2 游戏内验证流程放入文件后启动游戏按下面顺序验证进入曲库看列表中是否出现 “Groovy!”。如果出现先试听 preview.ogg确认预览片段不是空音频。进入谱面预览或选歌界面看封面是否显示正常。开始游玩听音频与判定线是否一致。打一组连续音符观察判定是否与音乐鼓点吻合。退出到曲库再进入一次确认不会因为重复扫描导致崩溃。第一次验证重点是“能不能进”第二次验证重点是“能不能玩”第三次验证重点是“稳不稳定”。三重验证都通过才算一首自定义歌曲基本做成。5.3 日志检查如果游戏里没有出现歌曲最有效的排查方式是看日志。日志文件通常位于游戏安装目录/Logs/ 用户目录/AppData/Local/DeathDisco/Saved/Logs/打开日志后搜索MDO、CustomSong、manifest、chart等关键字段。日志里可能出现的提示包括[CustomSong] scanning path: .../CustomSongs [CustomSong] found Groovy.mdo [CustomSong] failed to parse manifest.json: unexpected token [CustomSong] missing audio file: audio.ogg每条日志都直接指向一个具体问题。看到found但没加载成功说明扫描到了文件但解析内部资源失败看到missing说明文件路径与 manifest 不一致看到unexpected token说明 JSON 格式错误。注意不要只看游戏界面是否卡住。有些游戏在扫描自定义歌曲时遇到错误会静默跳过界面没有任何提示只有日志里保留了原因。养成先看日志再改文件的习惯能省掉大量反复重启游戏的时间。6. 常见问题与排查路径6.1 问题排查表下面把制作 MDO 过程中最常见的几类问题整理成表。问题现象常见原因检查方式处理建议曲库列表不显示歌曲自定义歌曲路径不对或 manifest 解析失败查看游戏日志中的扫描路径和解析错误将 MDO 移动到正确目录修正 JSON 格式歌曲显示但无法开始chart.json 字段名或音符结构不匹配对照官方谱面 JSON 结构检查按游戏实际字段结构重写谱面不要照搬示例音频与判定不同步offset 不准或 BPM 测定错误在编辑器中重新定位第一个强拍调整 offset用节拍器校准 BPM封面不显示图片格式非 PNG或 manifest 文件名不匹配检查 cover.png 能否打开转换图片格式并确保文件名一致日文标题乱码manifest.json 编码不是 UTF-8用文本编辑器查看文件编码保存为 UTF-8 without BOM音频无法播放ogg 文件损坏或码率过高用播放器试听 audio.ogg重新用 ffmpeg 转码6.2 一条典型排查链路以“放入 MDO 后游戏里不出现”为例完整排查顺序如下。第一步先确认文件确实在游戏会扫描的目录里。打开游戏设置或日志找到自定义歌曲目录路径把Groovy.mdo放进去。第二步重启游戏并立刻查看日志。如果日志中根本没有出现Groovy.mdo说明目录或扩展名不对。有些游戏只读取.zip而不是.mdo或者只读取文件夹而不读取压缩包。第三步如果日志出现了Groovy.mdo但后面跟着解析失败就打开 manifest.json 检查格式。最常见的错误是缺少逗号、多写引号、BPM 写成字符串。第四步如果 manifest 解析通过但显示缺少文件就要检查 manifest 里的文件名与目录中的实际文件名。第五步如果文件都存在但谱面加载失败优先检查 chart.json 里notes字段的格式和音符字段名。这个链路的顺序是固定的先路径再扩展名再清单再资源最后是谱面数据。跳过任何一步都可能因为“看起来没问题”而浪费更长时间。6.3 制作过程中最容易被忽略的三个坑第一个坑是偏移量的正负号。不同游戏对 offset 的定义可能完全不同。有的游戏里正数表示音符提前有的游戏里正数表示音符延后。如果导入后发现音符整体偏早或偏晚先尝试把 offset 取反不要急着调每一个音符。第二个坑是预览音频过长。preview.ogg 本应只是十几二十秒的试听片段但有人直接把完整音频复制成 preview.ogg。虽然能播放但曲库切换和选歌页面可能会卡顿。用 ffmpeg 裁剪出歌曲高潮或副歌段会更合适ffmpeg -ss 60 -t 20 -i audio.ogg -ac 2 -ar 44100 preview.ogg这里-ss 60表示从第 60 秒开始-t 20表示截取 20 秒。第三个坑是 BPM 虽然正确但谱面的“第 0 拍”概念与游戏不一致。有的游戏用绝对秒数有的游戏用拍数列加 offset还有的游戏把第一个音符作为隐式 offset。这些规则不是靠猜能猜出来的必须拆开一个能正常运行的 MDO 对照学习。7. 分享合规与最佳实践7.1 避免直接分发受版权保护的音频文件制作视频网站或社区分享时一个绕不开的问题是音频版权。広瀬香美的《Groovy!》是商业发行歌曲音频文件本身受版权保护。直接把自己转码后的完整音频打包进 MDO 分享给他人可能会导致分享链接被下架也可能给上传者带来法律风险。推荐的做法是在公开的 MDO 包中只包含谱面数据、封面、预览节奏文件或空占位音频不包含完整商业音频。文件放在网盘整体发布时不带音频玩家下载后自己把自己拥有的歌曲转成audio.ogg放入 MDO 解压目录中再游玩。这样虽然增加了使用成本但对作者和玩家都更安全。同时在发布说明中要写清楚依赖的音频来源、需要玩家自行准备的曲目名称、转换命令和放置方式。7.2 发布说明与版本锁定分享 MDO 时建议在压缩包内额外放一个README.txt写明以下信息谱面作者昵称。适用游戏版本或已知兼容版本。歌曲标题、艺术家、BPM、offset。谱面难度列表。玩家需要自备的音频文件名和格式。导入步骤。已知问题例如某个难度没有做完、某些版本会崩溃等。示例Groovy! - Kohmi Hirose (Custom Chart) Charter: your_nickname Game version: 需要根据你的版本填写 BPM: 128 Offset: 0.14s Difficulty: Normal 4 Install: 1. 将本目录中的 chart.json、cover.png、preview.ogg 复制到歌曲工作目录。 2. 使用 ffmpeg 将你自己拥有的歌曲转成 audio.ogg。 3. 打包成 Groovy.mdo或直接放入 CustomSongs 文件夹。版本锁定很重要。同一个 MDO 在不同版本的游戏里可能因为谱面字段变化而失效。写明“在当前版本验证通过”比“全版本通用”更诚实也能减少后续反馈量。7.3 制作 MDO 的可复用清单每次发布新歌曲前按照这张清单检查一遍。音频已经是目标格式采样率 44100Hz码率 128k 到 256k。音频响度峰值不超过 -3dB听感不闷不破。BPM 来自实际测定不是从网文猜测。offset 以第一个强拍位置为准并在游戏内校准过。manifest.json 使用 UTF-8 编码不包含 BOM。manifest 中的文件名与目录实际文件完全一致。chart.json 的字段与游戏实际支持的格式一致。音符没有负时间没有超出音频总时长。preview.ogg 是 15 到 30 秒的片段不是完整音频。目录名只包含英文、数字和下划线。在目标版本游戏中完成“显示、试听、游玩、退出再进”四步验证。公开分享包不含受版权保护的完整音频。制作自定义歌曲和写代码一样第一次不要追求完美。先做一首最简单、最短的歌曲把 MDO 从打包到导入的链路跑通再回头做《Groovy!》这种完整编曲。验证完整个流程后再去研究多难度谱面、变速段和复杂音符组合会顺利得多。下一步可以考虑写一个根据音频能量自动生成初版谱面的脚本把重复劳动力先交给工具自己专注于谱面和音乐表现的调整。
网站建设高端定制企业官网