akustikrausch/yamaha-smaf-player
GitHub: akustikrausch/yamaha-smaf-player
一个无依赖的C++20开源播放器,通过自研FM合成器引擎完整还原Yamaha MA-3/MA-5芯片的SMAF和弦铃声,解决了这一历史格式二十年来缺乏真实FM合成开源播放工具的问题。
Stars: 37 | Forks: 0
# Yamaha SMAF Player (C++)
**作者:Akustikrausch (Andreas Wendorf)**
一个使用 C++ 编写的、小巧且无依赖的 **Yamaha SMAF 铃声播放器**(`.mmf`)。
你只需把那些在 2004 年让你的三星、夏普或松下手机发出*哔哩-哔哩-吧*声音的文件交给它,它就能为你输出纯净的立体声音频。不需要手机,不需要固件,也不需要采样 ROM。纯粹是由软件重塑的 FM 芯片。
这代表了一整代几乎无人愿意通过开源代码来保留的声音。至今仍有数以百万计的此类文件四处流传,但**从未有人为它们完成过一个开源播放器**:过去的尝试要么停滞在 2004 年,要么完全绕过了合成器。因此,这里提供了一个 C++ 版本,从格式底层开始编写,并拥有专属的 FM 合成器核心。
## 为什么会有这个项目
**Yamaha MA 系列**(YMU762 = MA-3,YMU765 = MA-5)曾是 21 世纪初大量手机中的声卡芯片。它是一个纯正的 FM 合成器,与 adlib 和 sega-genesis 时代的 OPL/OPN 芯片是近亲,只是为了手机进行了微缩化。它所播放的 **SMAF**(`.mmf``)文件包含 FM 乐谱以及可选的 ADPCM 采样。和弦铃声便诞生于此。
后来智能手机问世,MP3 铃声占据了主导地位,整个格式便悄无声息地消亡了。Yamaha 下架了其 SDK。而且与 Amiga 或 C64 社区不同的是,几乎没有人编写过模拟器。现有的成果包括:
- **MAME** 没有可用的 MA-3 核心。
- **Yamaha 自己的工具** 是闭源的 DLL,现已流失。
- **mmfplay**(C 语言,2004 年)曾是走得最远的,然后也停滞了:它将 MA-3 乐谱映射到 OPL3 模拟器上“作为概念验证”,处理 SMF 转换的文件时会出错,没有附带任何许可证,而且用其 README 中的原话来说“听起来很糟糕”。最后一次更新已经是二十年前了。
- **`umjammer/vavi-sound`**(Java)可以解析并播放 SMAF,它是一个出色的行为参考,但它完全没有进行 FM 合成:它将音色重新封装为 general-midi,并让 Java 的合成器去近似模拟它们。而且它是 Java。
- **ffmpeg** 仅解析 ADPCM 部分,并直接拒绝处理 FM 序列器。
因此,一个原生、授权宽松、**无需 ROM** 且能真实播放 FM 芯片声音的播放器此前并不存在。这就是本项目。解析器的每一行代码、合成器的每一个算子,都是根据公开的 SMAF 格式说明以及对十六进制编辑器中真实文件的反复观察从零编写的,从未抄袭过任何其他播放器。
## 工作原理
引擎由五个小模块组成,每个模块均可独立测试:
```
src/
smaf_file the MMMD container: big-endian TLV chunks (CNTI/OPDA/MTR/ATR),
two score generations (HandyPhone for MA-1/2, Mobile for MA-3/5)
ma_player the two event decoders (+ okumura huffman for the packed
variant), a scheduler that runs in milliseconds like the chip
did, and the render loop that drives a pool of FM voices
ma_fm_core the FM DSP: 2-op and 4-op operators, exponential ADSR envelopes,
the OPL-style connection algorithms, and its own general-midi
approximation patch bank
smaf_voice decodes the yamaha voice sysex blobs (VM35 for MA-3/5, VMA for
MA-1/2) into FM patches, including the MA-3 bit-packed form
yamaha_adpcm the YM2608-family 4-bit adpcm codec, for the sampled voices
```
时间模型是一个令人惊喜的发现:SMAF 没有节奏(Tempo)事件,时间纯粹是度量化的(事件时间 = 持续时间之和乘以时间基数,以毫秒为单位),因此一个 tick 直接映射为一毫秒,进而映射为采样点。FM 核心直接以你的输出采样率运行,因此在合成器路径上无需进行重采样。
## ROM 问题(必读)
MA 芯片还自带了一个小型的 **general-midi 采样 ROM**,这样铃声就可以直接“播放 5 号程序”并获得预设的钢琴声。该 ROM 属于 Yamaha,从未被公开转储,且本项目**不发布它、不接收它,也永远不会打包它**。这是一条不可逾越的底线。
相反,`ma_fm_core` 内置了**其专属的手工打造 FM 音色库**来替代 ROM:请求“5 号程序”的文件会获得一个音色相近、合理的 FM 声音。它不可能与 2003 年的手机声音达到比特级的一致,这是坦诚的妥协。你获得的是真实的 FM 乐谱、真实的音符时机、真实的 ADSR,并由专属的合成器进行播放。能被识别,并且能顺利播放。
包含*自带*自定义音色的文件(其中很多都是如此)听起来会与原作完全一致,因为这些音色存在于文件本身,而不是 ROM 中。
## 快速开始
```
cmake -B build && cmake --build build
./build/smaf-render your_ringtone.mmf out.wav 30
```
或者将该库接入到你自己的代码中:
```
#include "smaf_file.h"
#include "ma_player.h"
using namespace fxchain::smaf;
SmafFile file;
file.parse(bytes.data(), bytes.size()); // MMMD container
MaPlayer player;
player.init(file, 44100); // build voices + decode the score
float buf[1024 * 2]; // interleaved stereo
int frames = player.render(buf, 1024); // pump the synth
```
## 状态
- **目前已支持:** FM 路径(全部 8 种 MA/YMF825 连接算法、线性 MULTI、每个算子的 feedback、驯化过的 modulation index 以及轻柔的 analog-lite 输出滤波器,因此听起来像芯片一样温暖,而不是尖锐刺耳),HandyPhone (MA-1/2) 和 Mobile (MA-3/5) 乐谱,huffman 压缩变体,自定义音色,多轨道,pitch bend,音量和声像。**外加嵌入式 PCM 音色**:yamaha-adpcm 波形库会被解码,且采样过的鼓/乐器音色可以与 FM 同步播放。
- **下一步计划:** 少数纯鼓组文件携带采样所用的批量音色波区块(`43 79 06 7F 03`);以及 SD-1 专属的高阶波形。请参阅 [ROADMAP.md](ROADMAP.md)。
- **不在计划内:** ROM 音色(见上文)、图形数据块、MA-7 流媒体以及 MFi(`.mld`)兄弟格式(一个表亲格式;见 [ROADMAP.md](ROADMAP.md) m3)。
## 安全性
该解析器旨在处理不受信任的文件,因此采用了防御性编程:每个区块长度都被限制在实际缓冲区范围内,huffman 解压带有节点和深度防护以及输出上限,事件解码器带有迭代和总事件上限。遇到问题时,它会优雅地退化为“不是有效的 SMAF 文件”提示,而不是直接崩溃。详情和报告请参阅 [SECURITY.md](SECURITY.md)。
## 目录内容
`src/` 包含引擎(四个小型编译单元外加一个仅头文件的 codec,无第三方依赖)。`example/smaf-render.cpp` 是不到 100 行代码的 WAV 命令行工具。`CMakeLists.txt` 用于构建静态 `smaf` 库及示例文件。全部内容即这些。
## 许可证
Apache License 2.0(见 [LICENSE](LICENSE))。解析器和合成器均为原创工作;未引入任何第三方代码,也不包含任何 Yamaha 的 ROM 或 SDK 资料。格式知识均来自公开的 SMAF 规范和实际观察。
## 免责声明
本项目不附属于 Yamaha,未经 Yamaha 授权或认可。“SMAF”和 MA 芯片名称归 Yamaha 所有。这是一项互操作性工作:这是一个针对开放文件格式的独立播放器,在构建过程中没有使用任何 Yamaha 代码或受保护的采样 ROM。
*向所有依然记得自己当年手机铃声听起来是什么样的人们致敬。*
标签:Bash脚本, C++, FM合成器, Yamaha SMAF, 多媒体, 数据擦除, 音频播放器, 音频模拟器