Описание формата VGM.

Формат звукового лога VGM (Video Game Music) был создан участниками коммьюнити S8-dev, Dave и Maxim, в августе 2001 года. Он относится к простым форматам, содержащим в себе только данные, выводимые в порты звукового устройства. Это упрощает его воспроизведение - для проигрывания достаточно эмулировать только звуковое устройство, но увеличивает объём файла - в секунду может происходить вывод от нескольких сотен до нескольких тысяч значений. В отличии от GYM (популярный формат звукового лога Megadrive/Genesis) и SSL (ныне забытый формат звукового лога для Sega Master System), VGM имеет гораздо более точный тайминг выводов в порты, что позволяет корректно сохранять в нём оцифровки и нестандартные приёмы программирования звука. VGM может содержать в себе данные для следующих чипов:

- SN76489/SN76496 (Master System, Mark III, SG-1000, SG-3000, Game Gear, Mega Drive/Genesis, BBC Micro);
- Yamaha YM2413 (японские версии Master System и Mark III, MSX-1 с FM-PAC);
- Yamaha YM2612 (Megadrive/Genesis);
- Yamaha YM2151 (Capcom CPS-1).

На текущий момент VGM является рекомендуемым форматом звукового лога для всех перечисленных систем и поддерживается многими эмуляторами. Для воспроизведения VGM существуют плагины для WinAMP, и отдельные программы-плееры.

Для уменьшения объёма файла VGM предусмотрено его сжатие с помощью GZip. Такие файлы имеют расширение VGZ. Такое расширение было выбрано вместо более логичного VGM.GZ для избежания открытия файла архиваторами вместо WinAMP - многие архиваторы умеют работать с GZip-архивами, и поэтому по умолчанию ассоциируются с расширением GZ. Для наших целей нам понадобятся неупакованные VGM-файлы, тогда как в Интернет-архивах музыки от Sega Master System обычно встречаются VGZ-файлы. Извлечь VGM из VGZ файла можно с помощью WinZIP или WinRAR, открыв его как архив - внутри будет файл без расширения, это и есть нужный нам неупакованный VGM.

Разберём внутреннюю структуру формата VGM. Он состоит из заголовка, данных лога, и опционального тега GD3, содержащего в себе текстовую информацию о файле. Последний нас не интересует, его формат мы рассматривать не будем. Все значения в заголовке хранятся как 32-битные целые числа. Порядок байт во всех многобайтовых числах - LSB, сначала идут младшие байты числа, потом старшие. Все значения смещений в файле - относительные, т.е., они указывают на адрес смещения + его значение.

Заголовок имеет размер 64 байта. Он имеет следующие поля:

0x00 : Сигнатура 'Vgm '
0x04 : Смещение конца файла (размер файла минус 4)
0x08 : Номер версии (текущий = 0x00000101)
0x0c : Тактовая частота PSG-чипа в Герцах, 0 если PSG-чип отсутствует
0x10 : Тактовая частота FM-чипа в Герцах, 0 если FM-чип отсутствует
0x14 : Смещение GD3-тэга, 0 если отсутствует
0x18 : Общее количество сэмплов в логе (один сэмпл = 1/44100 секунды)
0x1c : Смещение точки зацикливания, 0 если цикл отсутствует
0x20 : Количество сэмплов в цикле, 0 если цикл отсутствует
0x24 : Частота кадров, может быть 50, 60 или 0

Остальные байты заголовка не используются, должны быть заполнены нулями. В плеере нам понадобятся только значение тактовой частоты PSG-чипа и смещение точки зацикливания.

После заголовка следуют, собственно, данные лога. Это последовательность команд, каждая из которых имеет свой код, и может иметь размер от 1 до 3 байт. В логе могут встретиться следующие команды:

0x4f dd    : Запись значения dd в порт управления стерео-панорамой для Game Gear
0x50 dd    : Запись значения dd в порт SN76496
0x51 aa dd : Запись значения dd в регистр aa в порт YM2413
0x52 aa dd : Запись значения dd в регистр aa в порт 0 YM2612
0x53 aa dd : Запись значения dd в регистр aa в порт 1 YM2612
0x54 aa dd : Запись значения dd в регистр aa в порт YM2151
0x5? aa dd : Зарезервировано для других чипов
0x61 nn nn : Пауза в nn сэмплов (0...65535)
0x62       : Пауза в 735 сэмплов (1/60 секунды), короткий аналог для 0x61 0xdf 0x02
0x63       : Пауза в 882 сэмплов (1/50 секунды), короткий аналог для 0x61 0x72 0x03
0x66       : Конец файла

Нас будут интересовать только команды 0x50, 0x61...0x63, 0x66. Но в логе могут встретиться и другие команды (например, 0x4f или 0x51). Чтобы избежать неправильной интерпретации последующих команд, необходимо пропускать соответствующее количество байт для неиспользуемых команд.

[надо объяснить, что данные сохраняются блоками, между которыми есть паузы]