Парсинг метаданных MIDI-файлов на Ассемблере
Парсинг метаданных MIDI-файлов на Ассемблере
MIDI - "цифровой интерфейс музыкальных инструментов" - стандарт цифровой звукозаписи на формат обмена данными между электронными музыкальными инструментами. В отличие от других форматов это не оцифрованный звук, а наборы команд (проигрываемые ноты, ссылки на проигрываемые инструменты, значения изменяемых параметров звука), которые могут воспроизводиться по-разному в зависимости от устройства воспроизведения. Это позволяет реализовывать устройства, производящие аранжировку по заданным аккордам, а также приложения для воспроизведения и визуализации звука.
Согласно спецификации, MIDI-файл должен начинаться с заголовка. Его можно определить по первым 4 байтам сигнатуры "MThd", за которой следует DWORD с размером данных заголовка и сами данные. Несмотря на то, что в теории заголовок предполагает динамический размер, во всех без исключения MIDI-файлах размер данных в заголовке всегда равен 6 байтам, а размер самого заголовка, соответственно, равен 14 байтам. На этом с заголовком можно закончить.
Дальше следуют один или несколько контейнеров, в которых содержатся данные об инструментах, нотах, событиях, управляющие команды, текстовая информация и т.п. Каждый контейнер начинается с сигнатуры "MTrk", после которой следует DWORD с размером данных. В контейнере содержатся блоки в формате WORD маркера, BYTE типа данных, BYTE размера данных и сами данные. К примеру, блок с произвольной текстовой информацией должен начинаться с последовательности "00 FF 01", информация об авторских правах - "00 FF 02", а название трека или инструмента - "00 FF 03". Вроде бы есть определенная структура. Но неоднозначность толкования стандарта MIDI породила полный разброд и шатание. Каждый программный продукт, создающий MIDI-файлы, записывает в них метаданные по своему усмотрению. У меня есть небольшая коллекция MIDI-файлов на пару сотен экземпляров, и на каждое мое предположение, что "вот так точно получится", обязательно находился один или несколько файлов, которые в эту теорию не вписывались. То есть метаданные запросто могли находиться не в первом контейнере, названия могли быть записаны в блоки с маркерами "00 FF 01", "00 FF 02" или "00 FF 03", причем сразу несколько и в произвольной последовательности, под теми же маркерами могли идти копирайты, названия инструментов, слова для караоке, да вообще что угодно.
Метаданные в файле
Метаданные в файле
Короче, мне так и не удалось выяснить алгоритм, который с помощью которого можно однозначно и гарантированно определить, где именно содержится название композиции. Если у вас есть такая информация, то большая просьба поделиться. По итогу я решил поочередно перебирать контейнеры, в каждом из них искать первый блок с любым из трех перечисленных маркеров и принимать его текстовое содержимое в качестве названия трека. Решение спорное, не дающее 100% гарантии, но другого, как я уже написал выше, у меня нет. Для большинства случаев оно дает более-менее вменяемый результат. Код парсера будет примерно следующим:
Code (Assembler) : Убрать нумерацию
- ; Прочитать начало файла
- invoke _lopen,fname,OF_READ
- mov [desc],eax
- invoke _lread,[desc],buff,8
- ; Проверка на соответствие формата
- cmp dword[buff],'MThd'
- ; Неизвестный формат файла
- jne .loc_exit
- ; Перейти к первому контейнеру
- mov eax,dword[buff+4]
- bswap eax
- invoke _llseek,[desc],eax,FILE_CURRENT
- .loc_read:
- invoke _lread,[desc],buff,8
- cmp eax,8
- jne .loc_close
- ; Это контейнер с данными?
- cmp dword[buff],'MTrk'
- jne .loc_exit
- ; Размер метаданных
- mov ebx,dword[buff+4]
- bswap ebx
- mov eax,ebx
- add eax,10
- ; Выделить память под метаданные
- invoke GlobalAlloc,GMEM_ZEROINIT,eax
- mov [hMem],eax
- invoke GlobalLock,[hMem]
- mov [pMem],eax
- ; Прочитать метаданные
- invoke _lread,[desc],[pMem],ebx
- ; Указатель на начало контейнера
- mov esi,[pMem]
- .loc_scan:
- ; Маркер блока
- lodsw
- cmp ax,0FF00h
- jne .loc_next
- ; Идентификатор блока
- lodsb
- cmp al,1
- je .loc_metadata
- cmp al,2
- je .loc_metadata
- cmp al,3
- je .loc_metadata
- ; Размер блока
- lodsb
- movzx eax,al
- add esi,eax
- jmp .loc_scan
- .loc_next:
- ; Освободить память
- invoke GlobalUnlock,[hMem]
- ; Перейти к следующему контейнеру
- mov eax,dword[buff+4]
- bswap eax
- invoke _llseek,[desc],eax,FILE_CURRENT
- jmp .loc_read
- .loc_metadata:
- lodsb
- movzx ecx,al
- mov edi,title
- rep movsb
- ; Освободить память
- invoke GlobalUnlock,[hMem]
- ; title -> какие-то метаданные
Просмотров: 318 | Комментариев: 6
Метки: Assembler, мультимедиа
Комментарии
Отзывы посетителей сайта о статье
voffka
(07.11.2024 в 17:46):
Мне кажется у тебя сдвиг с самого начала неправильный MTrk нужно не как 4 символа рассматривать, а как 5, с завершающим нулём, тогда дальше всё как по маслу пойдёт DWORD размер MTrk, BYTE FF, BYTE тип, BYTE размер + 1 завершающий 0 нужно учесть.
FF - Означает мета-событие не отправляемое в миди порт
типы:
00 - Sequence Number
01 - Text Event
02 - Copyright Notice
03 - Track Name
04 - Instrument Name
05 - Lyrics
06 - Marker
07 - Cue Point
FF - Означает мета-событие не отправляемое в миди порт
типы:
00 - Sequence Number
01 - Text Event
02 - Copyright Notice
03 - Track Name
04 - Instrument Name
05 - Lyrics
06 - Marker
07 - Cue Point
ManHunter
(06.11.2024 в 21:22):
Значит это будет какая-то очередная нестандартная ситуация, которая не вписывается в схему и не будет обрабатываться. Увы.
Лестер Глючный
(06.11.2024 в 21:07):
Только хотелось было написать про высчитывание длительности у того "злощастного" 93.mid (дата его изменения аж за апрель 2003 года), что "роняет" последний 32-разрядный Sekaiju, (а kuzu (разработчик) отказывается поддерживать 32-разрядную, мол для TQPN выше 960 не хватит 32 бит), а он засветился на снимке экрана :))
Во второй дорожке (MTrk) Delta-time до "End Of Track" больше 4 байт (8FFFFFFD00)= 4294966912, некоторые программы (в т.ч. mf2tXP) определяют это как -384 (отрицательная позиция)…
Ищу такой MIDI-секвенсор, где б спецификация Standard MIDI File поддерживалась полностью, видеть/редактировать SysEx`ы размером выше 4 килобайт, и особенно Sequencer Specific (а то Tyros/PSR-S* в егонное Meta-событие «Keyboard Voice» аж 400 байт вписывает)… если существует такой хотя бы под DOS, то ещё лучше… Пока что только Cubase из двух-трёх десятков DAW это умеет, и то старые версии, а то начиная с 7-й ввели уже далеко не полную на тот момент поддержку ямаховских XF-аккордов (до сих пор не знает 24 «недокументируемых» шт.)
Cakewalk/SONAR, Reaper, DP, nTrack, Mixtrack и ещё мн.др (а FL, S1, Ableton/Bitwig/Polyform и мн.др. где даже SysEx`ов нет — подавно) обходят эти XF и пр. SMF Sequencer specific Meta-события стороной при иморте таких файлов (соответственно, теряются навсегда). Кстати, XF-данные могут находиться как отдельно от .MID, так и внутри отдельным chunk`ом после всех имеющихся MTrk, хотя, вроде ничто не запрещает использовать "не-MTrk" chunk`и сразу же после MThd… Да и вообще, для меня "чистые MIDI-данные" хранятся только в MTrk, и MThd указывает лишь только "номер типа SMF", кол-во MIDI-дорожек, и разрешение "Ticks Per Quarter Note", наподобие «Sampling Rage» (опечатка понравилась), благодаря которому и выясняется точная продолжительность MIDI-файла (за счёт Delta-Time перед каждым MIDI-событием, и учитывания каждой смены темпа FF5103######).
Ах, да… 00 перед FF 01/02/03 — расстояние между событиями = 0 ticks, а если их размещать не в начале?
Во второй дорожке (MTrk) Delta-time до "End Of Track" больше 4 байт (8FFFFFFD00)= 4294966912, некоторые программы (в т.ч. mf2tXP) определяют это как -384 (отрицательная позиция)…
Ищу такой MIDI-секвенсор, где б спецификация Standard MIDI File поддерживалась полностью, видеть/редактировать SysEx`ы размером выше 4 килобайт, и особенно Sequencer Specific (а то Tyros/PSR-S* в егонное Meta-событие «Keyboard Voice» аж 400 байт вписывает)… если существует такой хотя бы под DOS, то ещё лучше… Пока что только Cubase из двух-трёх десятков DAW это умеет, и то старые версии, а то начиная с 7-й ввели уже далеко не полную на тот момент поддержку ямаховских XF-аккордов (до сих пор не знает 24 «недокументируемых» шт.)
Cakewalk/SONAR, Reaper, DP, nTrack, Mixtrack и ещё мн.др (а FL, S1, Ableton/Bitwig/Polyform и мн.др. где даже SysEx`ов нет — подавно) обходят эти XF и пр. SMF Sequencer specific Meta-события стороной при иморте таких файлов (соответственно, теряются навсегда). Кстати, XF-данные могут находиться как отдельно от .MID, так и внутри отдельным chunk`ом после всех имеющихся MTrk, хотя, вроде ничто не запрещает использовать "не-MTrk" chunk`и сразу же после MThd… Да и вообще, для меня "чистые MIDI-данные" хранятся только в MTrk, и MThd указывает лишь только "номер типа SMF", кол-во MIDI-дорожек, и разрешение "Ticks Per Quarter Note", наподобие «Sampling Rage» (опечатка понравилась), благодаря которому и выясняется точная продолжительность MIDI-файла (за счёт Delta-Time перед каждым MIDI-событием, и учитывания каждой смены темпа FF5103######).
Ах, да… 00 перед FF 01/02/03 — расстояние между событиями = 0 ticks, а если их размещать не в начале?
ManHunter
(05.11.2024 в 19:11):
Зато в Win7 midi-файлы прямо в точности по стандарту, смотреть приятно.
Rustamer
(05.11.2024 в 19:09):
Хотел проверить на MIDI-файлах из Win95, но вдруг увидел, что там собственный RMI-формат MIDI от MS.. Зато вспомнил мелодии, с которых начиналось мое знакомство с классической музыкой.
Добавить комментарий
Заполните форму для добавления комментария
Про ноль в предыдущем комментарии Лестер Глючный все написал.