
Un reproductor C++ sin dependencias para tonos Yamaha SMAF (.mmf): el formato de tonos polifónicos de los chips de teléfono MA-3 / MA-5 de los años 2000, reconstruido con su propio motor FM libre de ROM. El reproductor abierto que nadie terminó. Probado en FXChainPlayer.
por Akustikrausch (Andreas Wendorf)
Un pequeño reproductor sin dependencias para tonos Yamaha SMAF (.mmf) en C++.
Le das uno de esos archivos que hacían que tu Samsung, Sharp o Panasonic hiciera
deedle-deedle-dee en 2004, y te devuelve audio estéreo limpio. Sin teléfono, sin
firmware, sin ROM de muestras. Solo el chip FM, reconstruido en software.
Este es el sonido de una década entera que casi nadie se molestó en preservar en código abierto. Hay millones de estos archivos todavía dando vueltas, y nadie terminó nunca un reproductor abierto para ellos: los intentos se estancaron en 2004 o eluden el sintetizador por completo. Así que aquí tienes uno en C++, escrito desde el formato hacia arriba, con su propio núcleo de síntesis FM.
🎧 este código se envía en un producto real: FXChainPlayer. un reproductor de audio nativo de Windows que reproduce tus viejos tonos junto a tus FLAC, SIDs y módulos de Amiga. ahí es donde este motor funciona cada día, contra un corpus de cientos de tonos reales de teléfonos. descarga el reproductor para solo escuchar, o sigue leyendo para saber cómo funciona el chip.
La serie Yamaha MA (YMU762 = MA-3, YMU765 = MA-5) fue el chip de sonido dentro
de una gran parte de los teléfonos de principios de los 2000. Es un sintetizador FM real,
un primo cercano de los chips OPL/OPN de la era Adlib y Sega Genesis, reducido para un
teléfono. Los archivos que reproducía, SMAF (.mmf), llevan una partitura FM más
muestras ADPCM opcionales. El tono polifónico nació aquí.
Luego llegaron los teléfonos inteligentes, los tonos mp3 se impusieron y todo el formato murió silenciosamente. Yamaha retiró su SDK. Y a diferencia de las escenas de Amiga o C64, casi nadie escribió un emulador. Lo que existe:
umjammer/vavi-sound (java) analiza y reproduce SMAF, y es una referencia de
comportamiento brillante, pero no hace síntesis FM en absoluto: reenvuelve los tonos
como general-midi y deja que el sintetizador de java los aproxime. Y es java.Así que no existía un reproductor nativo, con licencia permisiva y sin ROM del chip FM real. Este es ese. Cada byte del analizador y cada operador del sintetizador fueron escritos a partir de la descripción pública del formato SMAF más muchas horas mirando archivos reales en un editor hexadecimal, nunca copiando otro reproductor.
El motor son cinco piezas pequeñas, cada una comprobable por sí sola:
src/
smaf_file el contenedor MMMD: fragmentos TLV en big-endian (CNTI/OPDA/MTR/ATR),
dos generaciones de partituras (HandyPhone para MA-1/2, Mobile para MA-3/5)
ma_player los dos decodificadores de eventos (+ huffman okumura para la variante
empaquetada), un planificador que corre en milisegundos como lo hacía el chip,
y el bucle de renderizado que maneja un conjunto de voces FM
ma_fm_core el DSP FM: operadores de 2 y 4 ops, envolventes ADSR exponenciales,
los algoritmos de conexión estilo OPL, y su propio banco de parches
de aproximación general-midi
smaf_voice decodifica los blobs sysex de voz yamaha (VM35 para MA-3/5, VMA para
MA-1/2) en parches FM, incluyendo la forma empaquetada en bits de MA-3
yamaha_adpcm el códec ADPCM de 4 bits de la familia YM2608, para las voces muestreadas
El modelo de temporización es la grata sorpresa: SMAF no tiene eventos de tempo, el tiempo es puramente métrico (tiempo de evento = suma de duraciones por una base de tiempo, en milisegundos), así que un tick se mapea directamente a un milisegundo y luego a muestras. El núcleo FM corre directamente a tu tasa de salida, por lo que no hay remuestreo en la ruta del sintetizador.
Los chips MA también llevaban una pequeña ROM de muestras general-midi para que un tono pudiera decir "reproduce el programa 5" y obtener un piano predefinido. Esa ROM es de Yamaha, nunca fue volcada públicamente, y este proyecto no la incluye, no la quiere y nunca la empaquetará. Esa es una línea dura.
En su lugar, ma_fm_core lleva su propio banco de parches FM construido a mano que
sustituye a la ROM: un archivo que pide "programa 5" obtiene una voz FM plausible de la
familia correcta. No será idéntico bit a bit a un teléfono de 2003, y ese es el compromiso
honesto. Lo que obtienes es la partitura FM real, la temporización real de notas, el ADSR
real, interpretados por un sintetizador propio. Reconocido, y suena.
Los archivos que llevan sus propias voces personalizadas (muchos lo hacen) suenan exactamente como fueron creados, porque esas voces viven en el archivo, no en la ROM.
cmake -B build && cmake --build build
./build/smaf-render tu_tono.mmf salida.wav 30
o conecta la librería a tu propio código:
#include "smaf_file.h"
#include "ma_player.h"
using namespace fxchain::smaf;
SmafFile file;
file.parse(bytes.data(), bytes.size()); // contenedor MMMD
MaPlayer player;
player.init(file, 44100); // construir voces + decodificar la partitura
float buf[1024 * 2]; // estéreo entrelazado
int frames = player.render(buf, 1024); // bombear el sintetizador
43 79 06 7F 03) que algunos archivos de
batería pura llevan con sus muestras; y las formas de onda superiores específicas de SD-1.
ver ROADMAP.md..mld) (un primo; ver
ROADMAP.md m3).