
Парсинг метаданных файлов VQF на Ассемблере

Парсинг метаданных файлов VQF на Ассемблере
VQF (Vector Quantization File) - это аудиоформат, разработанный в середине 1990-х годов японской компанией NTT (Nippon Telegraph and Telephone) в сотрудничестве с Yamaha. Он основан на кодеке TwinVQ (Transform-domain Weighted Interleave Vector Quantization), который позже был включен как объектный тип в стандарт MPEG-4 Audio. Формат отличался высокой эффективностью сжатия: при битрейтах 80-112 кбит/с проприетарная реализация VQF (SoundVQ) часто обеспечивала лучшее субъективное качество по сравнению с MP3, особенно ниже 96 кбит/с.
Как уже указано, формат считается проприетарным, и ни нормальной документации, ни официального описания структуры файлов найти не удалось. Пришлось разбираться на основе готовых файлов, тем не менее, ничего особо сложного в них нет. Внутреннее устройство VQF-файлов достаточно прозрачно: формат следует четкой структуре.

Метаданные в файле
Файл в формате VQF состоит из сигнатуры, заголовка, содержащего несколько секций, и данных. Сигнатура представляет собой строку "TWIN97012000" из 12 символов, за которой следует DWORD с указанием размера заголовка в формате big-endian. Здесь и далее в статье будет подразумеваться, что все размеры данных указаны в формате big-endian, это сделано, чтобы не повторяться. Затем идет заголовок, состоящий из нескольких секций. После него следует DWORD со сигнатурой "DATA", за которым располагаются сами аудиоданные. Это уже не интересно, нам нужны только метаданные.

Метаданные в файле
В заголовке дальше идут секции. Каждая секция также начинается с четырехбайтовой сигнатуры и DWORD с размером данных секции. Я нашел несколько сигнатур: "COMM", "NAME", "(c)_", "FILE", "DSIZ" или "AUTH", точные названия и их количество установить не удалось. Обратите внимание, строка "(c)_" тоже является сигнатурой (последний символ - пробел). Если данные секции пусты, размер данных остается нулевым, записывается DWORD со значением 0. Повторяем это для каждой секции до окончания заголовка. Для проверки можно заглянуть в аудиоданные - там будет первая сигнатура "DATA". Осталось только оформить это в виде кода.
Code (Assembler) : Убрать нумерацию
- ; Прочитать сигнатуру файла
- invoke _llseek,[desc],0,FILE_BEGIN
- invoke _lread,[desc],buff,16
- mov esi,buff
- lodsd
- cmp eax,'TWIN'
- jne loc_close_vqf
- lodsd
- cmp eax,'9701'
- jne loc_close_vqf
- lodsd
- cmp eax,'2000'
- jne loc_close_vqf
- ; Размер заголовка
- lodsd
- bswap eax
- ; Размер начала данных
- add eax,4
- ; Проверить корректность заголовка
- cmp eax,BUFF_SIZE
- ; Размер слишколм большой
- ja loc_close_vqf
- ; Прочитать данные
- mov ebx,eax
- invoke _lread,[desc],buff,eax
- cmp dword [buff+ebx-4],'DATA'
- jne loc_close_vqf
- mov esi,buff
- loc_parse_vqf:
- ; Проверить окончание заголовка
- lodsd
- cmp eax,'DATA'
- je loc_close_vqf
- mov edi,album
- cmp eax,'AUTH'
- je @f
- mov edi,artist
- cmp eax,'(c) '
- je @f
- mov edi,title
- cmp eax,'NAME'
- je @f
- ; Пропустить данные
- lodsd
- bswap eax
- add esi,eax
- jmp loc_parse_vqf
- @@:
- ; Длина строк
- lodsd
- bswap eax
- ; UTF-8 -> юникод
- push eax
- push esi
- invoke MultiByteToWideChar,CP_UTF8,0,esi,eax,0,0
- invoke MultiByteToWideChar,CP_UTF8,0,esi,-1,edi,eax
- pop esi
- pop eax
- ; Следующий тег
- add esi,eax
- jmp loc_parse_vqf
- loc_close_vqf:
- invoke CloseHandle,[desc]
- ; title -> название композиции
- ; artist -> исполнитель
- ; album -> альбом
В приложении пример программы с исходным текстом, которая парсит и выводит метаданные из файла в формате VQF.
Просмотров: 231 | Комментариев: 3
Метки: Assembler, мультимедиа
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(12.01.2026 в 15:42):
Спасибо, пригодится
Лестер Глючный
(12.01.2026 в 15:38):
COMM — обязательная метка:
long channelMode # 0: mono, 1:stereo
long bitRate # integer in kbit/s
long samplingRate # integer in kHz, (e.g. 44.1 kHz -> 44)
long securityLevel # always 0
DSIZ — размер сжатых аудиоданных…
т.о., esvorbei_drum.vqf: mono 20kbps 22kHz, 300325 байт сжатых аудиоданных, KakOsennij.vqf: stereo 48kbps 22kHz, 1071314 б.
правильно ли подсчитана длительность: ?2?7?48? и 2?58?33? соответственно?
COMT — комментарий # (в отличие от COMM у ID3)
Порадовало наличие метки FILE (compressed file name) — вот такое пару десятков лет назад было очень полезно, не только из-за вмещаемости больше 8.3 символов, но банально из-за повреждения файловых систем на флешках (оказавшиеся в папках FOUND.###) ну или интернетовских "цифробуквы.mp3" тьфу, .vqf… Хорошо, что "не NULL-terminated", т.о. можно смело использовать Юникод (хоть UTF-8, хоть UTF-16LE или даже BE)…
WORD (songwriter) # TEXT
MUSC (composer) # TCOM
ARNG (arranger)
PROD (producer)
PRSN (player | personnel)
NOTE (liner notes)
REMX (remixer) # TPE4
CDCT (conductor) # TPE3
BAND (band, orchestra, group) # TPE2
SING (singer) # TPE1
ISRC (International Standard Record Code) # TSRC
ABLM # TALB
LABL # TPUB
LYRC # USLT
YEAR # YEAR/TYER
short year
char month
ENCD (compressed date) # TDAT
short year
char month
char day
char hour
char minute
char timeZone
TRAC # TRCK
short trackNumber
GUID (Globally Unique Identifier) # UFID
byte[16] guid
SCND (auxiliary information) # SCND/NAME <=> TIT3
while ( StringChunk subChunks[ ])
EXTR (reserved)
_ID3 (reserved for ID3v2; priority should be defined in case of conflict with TwinVQ)
char[ ] data
_YMH (отсылка на Я?маха)
_NTT (на Японский Телеграф и Телефон)
long channelMode # 0: mono, 1:stereo
long bitRate # integer in kbit/s
long samplingRate # integer in kHz, (e.g. 44.1 kHz -> 44)
long securityLevel # always 0
DSIZ — размер сжатых аудиоданных…
т.о., esvorbei_drum.vqf: mono 20kbps 22kHz, 300325 байт сжатых аудиоданных, KakOsennij.vqf: stereo 48kbps 22kHz, 1071314 б.
правильно ли подсчитана длительность: ?2?7?48? и 2?58?33? соответственно?
COMT — комментарий # (в отличие от COMM у ID3)
Порадовало наличие метки FILE (compressed file name) — вот такое пару десятков лет назад было очень полезно, не только из-за вмещаемости больше 8.3 символов, но банально из-за повреждения файловых систем на флешках (оказавшиеся в папках FOUND.###) ну или интернетовских "цифробуквы.mp3" тьфу, .vqf… Хорошо, что "не NULL-terminated", т.о. можно смело использовать Юникод (хоть UTF-8, хоть UTF-16LE или даже BE)…
WORD (songwriter) # TEXT
MUSC (composer) # TCOM
ARNG (arranger)
PROD (producer)
PRSN (player | personnel)
NOTE (liner notes)
REMX (remixer) # TPE4
CDCT (conductor) # TPE3
BAND (band, orchestra, group) # TPE2
SING (singer) # TPE1
ISRC (International Standard Record Code) # TSRC
ABLM # TALB
LABL # TPUB
LYRC # USLT
YEAR # YEAR/TYER
short year
char month
ENCD (compressed date) # TDAT
short year
char month
char day
char hour
char minute
char timeZone
TRAC # TRCK
short trackNumber
GUID (Globally Unique Identifier) # UFID
byte[16] guid
SCND (auxiliary information) # SCND/NAME <=> TIT3
while ( StringChunk subChunks[ ])
EXTR (reserved)
_ID3 (reserved for ID3v2; priority should be defined in case of conflict with TwinVQ)
char[ ] data
_YMH (отсылка на Я?маха)
_NTT (на Японский Телеграф и Телефон)
bodrox
(19.12.2025 в 12:24):
Да, помню тогда появились кодировщик и плагин к винампу. Быстро забросил этот кодек, так как уже тогда старался в библиотеке теги вбивать, а с ямахой были какие-то проблемы. Потом я очень быстро перешёл на foobar2000 и про vqf забыл полностью, особенно когда на телефонах и в машине воспроизводить только mp3 да wma было можно
Добавить комментарий
Заполните форму для добавления комментария
Пример программы с исходным текстом (FASM)

