Expand description
Parser and player of Famicon (a.k.a. NES) Flavored Music Macro Language (FFMML).
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)
§Examples
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
§References
§About MML
§Abount Famicon sound
Structs§
- Channel
State - State of a playing channel.
- Music
- Music object built from an MML script.
- Music
Player MusicPlayer
is an iterator that generates audio samples.- Parse
Music Error - An error returned from
Music::new()
. - Play
Music Error - An error returned from
MusicPlayer::take_last_error()
. - Sample
- Audio sample.
Enums§
- Channel
Name - Channel name.