
依存関係のないC++プレイヤー:2000年代の携帯電話チップMA-3/MA-5用のポリフォニック着信音形式であるYamaha SMAF (.mmf)ファイルを再生します。独自のROM不要FMエンジンで再構築されました。誰も完成させなかったオープンプレイヤー。FXChainPlayerで実証済み。
by Akustikrausch (Andreas Wendorf)
小型で依存関係のない Yamaha SMAF着信音 (.mmf) のプレイヤー です。C++ で書かれています。
2004年にあなたのSamsung、Sharp、Panasonicが デイドル・デイドル・ディー と鳴らしていたあのファイルを受け取り、クリーンなステレオ音声を出力します。電話機もファームウェアもサンプルROMも不要です。FMチップをソフトウェアで完全に再構築しています。
これは、オープンコードで保存しようとする者がほとんどいなかった、まるまる10年間のサウンドです。これらのファイルは今も何百万も漂っていますが、そのためのオープンプレイヤーを完成させた者は誰もいません。試みは2004年で頓挫するか、シンセサイザーを迂回しています。そこで、フォーマットから組み上げ、独自のFMシンセコアを備えたC++版をお届けします。
🎧 このコードは実際の製品に搭載されています:FXChainPlayer。 ネイティブWindowsオーディオプレイヤーで、古い着信音をFLAC、SID、Amigaモジュールと並べて再生できます。このエンジンはそこで日々、数百もの実機ハンズセットトーンに対して動作しています。聴くだけならプレイヤーを入手してください。チップの仕組みについては以下をお読みください。
Yamaha MAシリーズ(YMU762 = MA-3、YMU765 = MA-5)は、2000年代初頭の多くの携帯電話に搭載されていたサウンドチップです。これは本格的なFMシンセサイザーで、AdlibやSega Genesis時代のOPL/OPNチップの近縁種を、ハンズセット用に小型化したものです。このチップが再生したファイル、SMAF (.mmf) は、FMスコアに加えてオプションのADPCMサンプルを保持しています。ポリフォニック着信音はここで生まれました。
やがてスマートフォンが登場し、MP3着信音が主流になり、フォーマット全体が静かに消えていきました。YamahaはSDKをオフラインにしました。AmigaやC64シーンのように、エミュレータを書いた者はほとんどいません。現状は以下の通りです:
umjammer/vavi-sound (Java) はSMAFをパースして再生します。優れた動作リファレンスですが、FM合成は一切行いません。トーンをGeneral MIDIにラップし、Javaのシンセサイザーで近似します。しかもJavaです。つまり、実際のFMチップを再生する、ネイティブで寛容なライセンスの、ROM不要 のプレイヤーは存在しませんでした。これがそれです。パーサーのすべてのバイトとシンセサイザーのすべてのオペレータは、公開されているSMAFフォーマットの記述と、実際のファイルをHEXエディタでじっくり眺めることから書かれており、他のプレイヤーをコピーしたものではありません。
エンジンは5つの小さな部品で構成され、それぞれ単独でテスト可能です:
src/
smaf_file MMMDコンテナ:ビッグエンディアンのTLVチャンク(CNTI/OPDA/MTR/ATR)、
2つのスコア世代(HandyPhone(MA-1/2用)、Mobile(MA-3/5用))
ma_player 2つのイベントデコーダ(+ パック形式用の奥村ハフマン)、チップと同様に
ミリ秒単位で動作するスケジューラ、FMボイスプールを駆動するレンダリングループ
ma_fm_core FM DSP:2オペレータ/4オペレータ、指数ADSRエンベロープ、
OPLスタイル接続アルゴリズム、独自のGeneral MIDI近似パッチバンク
smaf_voice YamahaボイスSYSEXブロブ(MA-3/5用VM35、MA-1/2用VMA)をFMパッチにデコード
(MA-3のビット詰め形式を含む)
yamaha_adpcm YM2608ファミリーの4ビットADPCMコーデック(サンプルボイス用)
タイミングモデルは嬉しい驚きです。SMAFにはテンポイベントがなく、時間は純粋にメトリックです(イベント時間 = デュレーションの合計 × タイムベース(ミリ秒))。したがって、1ティックは直接1ミリ秒、そしてサンプルにマッピングされます。FMコアは出力レートで直接動作するため、シンセパスにリサンプリングはありません。
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コンテナ
MaPlayer player;
player.init(file, 44100); // ボイス構築 + スコアデコード
float buf[1024 * 2]; // インターリーブステレオ
int frames = player.render(buf, 1024); // シンセサイザーを駆動
43 79 06 7F 03)、およびSD-1固有の高度な波形形状。詳細は ROADMAP.md をご覧ください。.mld) 姉妹フォーマット(親戚にあたるもの。詳細は ROADMAP.md のm3を参照)。このパーサーは信頼できないファイルを読み込むことを想定しており、防御的に記述されています。チャンク長は常に実際のバッファにクランプされ、ハフマン伸張はノード数+深さのガードと出力上限を持ち、イベントデコーダは反復回数+総イベント数の上限を持ちます。クラッシュする代わりに「有効なSMAFファイルではありません」と表示されます。詳細および報告方法については SECURITY.md をご覧ください。
src/ エンジン(4つの小さな翻訳単位+ヘッダオンリーのコーデック、依存関係なし)。example/smaf-render.cpp 100行未満のWAV CLI。CMakeLists.txt で静的 smaf ライブラリとサンプルをビルドします。以上がすべてです。
Apache License 2.0(LICENSE 参照)。パーサーとシンセサイザーは独自の作品です。サードパーティのコードはベンダリングされておらず、YamahaのROMやSDKの素材は一切含まれていません。フォーマットの知識は公開されているSMAF仕様と観察に基づいています。
Yamahaとの提携、承認、または推奨を受けたものではありません。「SMAF」およびMAチップの名称はYamahaの商標です。これは相互運用性のための作業です。オープンファイルフォーマットの独立したプレイヤーであり、Yamahaのコードや保護されたサンプルROMを一切使用せずに構築されています。
着信音がどんな音だったか今でも覚えているすべての人に敬意を込めて。