Parser and player of Famicon Flavored Music Macro Language (FFMML).
Famicon is also known as NES (Nintendo Entertainment System).
๐ต Web Player
๐ VSCode Extension
The language specification of FFMML is based on MCK. But there are (known) differences between FFMML and MCK as follows:
- FFMML doesn't support the following features:
#INCLUDE
directive#OCTAVE-REV
directive@n
command (direct frequency select)n
command (direct note select)y
command (direct memory entry)- DPCM channel
- FFMML features
#CHANNEL <CHANNEL_NAME> <OSCILLATOR>
directive that defines custom channels:<CHANNEL_NAME>
:A..=Z
<OSCILLATOR>
:1
(pulse wave),2
(triangle wave), or3
(noise)
The following example parses an MML script and generates audio data:
let mml = r#"; From https://www.nesdev.org/mck_guide_v1.0.txt
#TITLE My First NES Chip
#COMPOSER Nullsleep
#PROGRAMER 2003 Jeremiah Johnson
@v0 = { 10 9 8 7 6 5 4 3 2 }
@v1 = { 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 }
@v2 = { 15 12 10 8 6 3 2 1 0 }
@v3 = { 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 }
ABCD t150
A l8 o4 @01 @v0
A [c d e f @v1 g4 @v0 a16 b16 >c c d e f @v1 g4 @v0 a16 b16 >c<<]2
C l4 o3 q6
C [c e g8 g8 a16 b16 >c8 c e g8 g8 a16 b16 >c8<<]4
D l4 @v2 @0
D [@v2 b @v3 e @v2 b @v3 e @v2 b @v3 e @v2 b @v3 e8 @v2 b8]4"#;
let music = mml.parse::<ffmml::Music>().unwrap_or_else(|e| panic!("{e}"));
let mut player = music.play(48000);
let audio_data = (&mut player).collect::<Vec<_>>();
player.take_last_error().map(|e| panic!("{e}"));
To play the music defined by the above MML script, run the following commands:
$ cargo install ffmmlc
$ cat examples/music01.mml | ffmmlc > music01.wav
$ play music01.wav