by Akustikrausch (Andreas Wendorf)
a small, dependency-free player for Yamaha SMAF ringtones (.mmf) in c++.
you hand it one of those files that made your samsung, sharp, or panasonic go
deedle-deedle-dee in 2004, and it hands you clean stereo audio. no phone, no
firmware, no sample rom. just the FM chip, rebuilt in software.
this is the sound of an entire decade that almost nobody bothered to preserve in open code. there are millions of these files still floating around, and nobody ever finished an open player for them: the attempts stalled in 2004 or route around the synth entirely. so here is a c++ one, written from the format up, with its own FM synth core.
🎧 this code ships in a real product: FXChainPlayer. a native windows audio player that plays your old ringtones next to your FLACs, SIDs, and Amiga modules. that is where this engine runs every day, against a corpus of hundreds of real handset tones. grab the player to just listen, or read on for how the chip works.
why this exists
the Yamaha MA series (YMU762 = MA-3, YMU765 = MA-5) was the sound chip inside
a huge slice of early-2000s phones. it is a proper FM synthesizer, a close cousin
of the OPL/OPN chips from the adlib and sega-genesis era, shrunk down for a
handset. the files it played, SMAF (.mmf), carry an FM score plus optional
adpcm samples. the polyphonic ringtone was born here.
then smartphones arrived, mp3 ringtones took over, and the whole format quietly died. yamaha took its sdk offline. and unlike the amiga or c64 scenes, almost nobody wrote an emulator. what exists:
- MAME has no working MA-3 core.
- yamaha's own tools were closed-source dlls, now gone.
- mmfplay (c, 2004) got the furthest and then stopped: it maps MA-3 scores onto an OPL3 emulator "as a proof of concept", chokes on SMF-converted files, carries no license, and by its own readme "sounds really bad". last touched two decades ago.
umjammer/vavi-sound(java) parses and plays smaf, and it is a brilliant behavioural reference, but it does not do FM synthesis at all: it re-wraps the tones as general-midi and lets java's synth approximate them. and it is java.- ffmpeg parses only the adpcm side and rejects the FM sequencer outright.
so a native, permissively-licensed, rom-free player of the actual FM chip did not exist. this is that. every byte of the parser and every operator of the synth was written from the public SMAF format description plus a lot of staring at real files in a hex editor, never by copying another player.
how it works
the engine is five small pieces, each one testable on its own:
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
the timing model is the nice surprise: SMAF has no tempo events, time is purely metric (event time = sum of durations times a timebase, in milliseconds), so one tick maps straight to one millisecond and then to samples. the FM core runs at your output rate directly, so there is no resampling on the synth path.
the rom question (read this)
the MA chips also carried a small general-midi sample rom so a ringtone could say "play program 5" and get a preset piano. that rom is yamaha's, it was never publicly dumped, and this project does not ship it, does not want it, and will never bundle it. that is a hard line.
instead, ma_fm_core carries its own hand-built FM patch bank that stands in
for the rom: a file that asks for "program 5" gets a plausible FM voice of the
right family. it will not be bit-identical to a 2003 handset, and that is the
honest trade. what you get is the real FM score, the real note timing, the real
adsr, played by a synth of its own. recognised, and it plays.
files that carry their own custom voices (a lot of them do) sound exactly as authored, because those voices live in the file, not the rom.
quick start
cmake -B build && cmake --build build
./build/smaf-render your_ringtone.mmf out.wav 30
or wire the library into your own code:
#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
status
- works today: the FM path (all 8 MA/YMF825 connection algorithms, the linear MULTI, per-operator feedback, a tamed modulation index and a gentle analog-lite output filter so it sounds warm like the chip, not shrill), HandyPhone (MA-1/2) and Mobile (MA-3/5) scores, the huffman-packed variant, custom voices, multi- track, pitch bend, volume, pan. plus embedded PCM voices: the yamaha-adpcm wave bank is decoded and sampled drum / instrument voices play alongside the FM.
- next: the bulk voice-wave block (
43 79 06 7F 03) that a few pure drum-kit files carry their samples in; and the SD-1-specific higher waveshapes. see ROADMAP.md. - out of scope: the rom timbres (see above), the graphics chunks, MA-7
streaming, and the MFi (
.mld) sibling format (a cousin; see ROADMAP.md m3).
security
this parser is meant to eat untrusted files, so it is written defensively: every chunk length is clamped to the real buffer, the huffman inflate has node + depth guards and an output ceiling, and the event decoders carry iteration + total-event caps. it degrades to "not a valid SMAF file" rather than crashing. details and reporting in SECURITY.md.
what's in the box
src/ the engine (four small translation units plus a header-only codec, no
deps). example/smaf-render.cpp the wav CLI in under 100 lines. CMakeLists.txt
builds a static smaf lib plus the example. that is the whole thing.
disclaimer
not affiliated with, authorized by, or endorsed by Yamaha. "SMAF" and the MA chip names are Yamaha's. this is interoperability work: an independent player of an open file format, built without any yamaha code or the protected sample rom.
greetz to everyone who still remembers what their ringtone sounded like.
Comments