VGM stands for Video Game Music.
In practice, a VGM file is usually not a recording like mp3 or wav.
Instead, it is closer to a log of chip operations.
For example, a VGM file can contain information such as:
So a VGM file is often closer to:
than to:
A simple mental model is:
It is useful to separate these ideas:
wav
already-rendered audio samplesvgm
instructions for sound chips plus timingA wav file says:
A vgm file says:
The YM2612 is controlled by register writes.
This matches VGM very well, because VGM also describes playback as a sequence of chip commands.
That means:
ex03_beep.cpp writes registers directlyIt is useful to separate these two ideas early:
FM
create a sound by configuring YM2612 registersPCM / DAC
create a sound by sending waveform sample bytes to the chipFor FM, we usually think in terms of:
For PCM / DAC, the mental model is different.
We do not select “instrument 3” or “sample 5” inside the YM2612. Instead, we send 8-bit sample values to the chip over time.
That is why PCM playback is closer to:
than to:
Two YM2612 registers are especially important here:
0x2A
DAC data0x2B
DAC enableThe rough idea is:
0x2B.0x2A.So for PCM, the “sound source” is not a built-in preset.
The sound source is the byte stream that we feed into 0x2A.
To support VGM playback, we will eventually need to understand:
VGM is a music data format that stores sound chip commands and timing.
For this repository, VGM matters because it could let us:
ymfmThis note is intentionally YM2612-first.
The full VGM specification is large, but for an initial YM2612 player, we do not need all of it.
At first, we only need to understand:
If the goal is “play YM2612 VGM through ymfm”, start with these fields:
0x08: version0x18: total number of samples0x1C: loop offset0x20: loop number of samples0x2C: YM2612 clock0x34: VGM data offsetEverything else can wait until later.
A VGM file begins with a header.
The header stores metadata such as:
For modern VGM files, the header is usually treated as a 0x100 byte area.
Some important fields are:
0x00: "Vgm " file identifier0x04: EOF offset0x08: version0x18: total number of samples0x1C: loop offset0x20: loop number of samples0x2C: YM2612 clock0x34: VGM data offset1.50, the command stream starts at 0x401.50 and later, the command stream start is determined from the data offset fieldTo replay YM2612 correctly, we will especially care about:
The YM2612 clock is important because it tells us what chip clock the music expects.
For a first implementation, this is already enough.
After the header comes a stream of commands.
The player reads the commands one by one. Each command usually means one of these things:
For YM2612 playback, the most important commands are:
0x52 aa dd
write value dd to YM2612 port 0 register aa0x53 aa dd
write value dd to YM2612 port 1 register aa0x61 nn nn
wait n samples0x62
wait 735 samples0x63
wait 882 samples0x66
end of sound dataFor YM2612 DAC / PCM playback, these are also important:
0x67
data block0x68
PCM RAM write0x80-0x8F
YM2612 DAC write with short wait0x90-0x95
DAC stream control0xE0
data bank seekFor a first YM2612-only implementation, these commands are enough:
0x52 aa dd0x53 aa dd0x61 nn nn0x620x630x66That means the first player only needs to support:
0x52 and 0x53 matterThese commands map very naturally to the YM2612 interface we already have.
0x52 aa dd
means chip.write(0, aa) then chip.write(1, dd)0x53 aa dd
means chip.write(2, aa) then chip.write(3, dd)This is one of the main reasons VGM fits well with YM2612 emulation.
For PCM, VGM usually does not mean:
Instead, it usually means something closer to:
That is why commands such as 0x67, 0x80-0x8F, 0x90-0x95, and 0xE0 matter.
0x67 and 0x68 are differentThese two commands are easy to mix up.
0x67
stores PCM-related data inside the VGM stream0x68
writes PCM data into a chip-side PCM RAM areaSo the rough distinction is:
0x67
“here is the source data”0x68
“copy data into chip memory”For YM2612 DAC playback, the most common mental model is still:
0x2B0x2AThat is why some YM2612 VGM files can still play even if 0x68 is not implemented.
A simple mental model is:
0x2A.So the PCM “instrument” is really:
This is the important connection:
0x2B
enable DAC mode0x2A
receive one PCM sample byte0x67
provide PCM data bytes0xE0
move the current read position in a data bank0x80-0x8F
write one DAC byte and wait a small amount0x90-0x95
describe DAC stream playbackIn other words:
The playback idea is roughly:
0x2B to enable DAC.0x2A.0x2A.That is the basic PCM / DAC model for YM2612.
VGM does not only store register writes. It also stores timing.
That timing is critical.
For example:
Without the wait commands, the music would not have the correct rhythm or note lengths.
A minimal YM2612 VGM player would do this:
0x52 and 0x53, write to YM2612.0x66, or jump to the loop point if looping is enabled..vgm is the plain format.vgz is usually gzip-compressed VGM data.vgz before parsing the VGM streamIf we keep the scope small, the first YM2612 VGM player can be:
0x52 and 0x53.0x61, 0x62, and 0x63.0x66.This is a good first target.
At the current stage of this repository:
More specifically:
0x67
basic data-block loading is supported0x80-0x8F
basic DAC write + wait is supported0x90-0x95
minimal DAC stream support exists0xE0
basic data-bank seek is supported0x68
not implemented yetSo PCM / DAC playback is not “fully done”, but it is also no longer “all skipped”.