Музыка МИДИ формата на компьютере

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

  Статья предназначена тем, кто хочет посмотреть на МИДИ формат изнутри - разобрать его тексты, познакомиться и поэкспериментировать с его структурами.
  В интернете есть описания этого формата, начните с Википедии, но мало примеров, и совсем отсутствуют возможности эксперимента с МИДИ текстами. Этот пробел я и хочу заполнить.


   СИМВОЛЫ И КОДЫ МИДИ ТЕКСТА

  Структура МИДИ текста не сложнее структуры текста, который Вы читаете на этой страничке, или на страничке в Блокнотике. Это - последовательность байт.
  В обычном тексте каждому байту соответствует буква - кириллица или латиница, цифра или какой-нибудь знак, пробел, - тот, который Вы видите на экране. Некоторые байты не экране не отображаются.
  С другой стороны, каждый байт имеет свой код - десятичное число от 0 до 255, или число в шестнадцатиричном виде - от 00 до FF. Переводить числа из одного представления в другое удобно с помощью экранного калькулятора в режиме "Программист". Попрактикуйтесь, чтобы привыкнуть к шестнадцатиричному представлению чисел.
  Как буквы обычного текста собираются в некие значащие слова, так и следующие друг за другом байты МИДИ текста составляют сообщения. Например

    90 45 7F - сообщает, что нужно взять ноту Ля 1-й октавы очень громко

  0 после девятки говорит, что для этого будет использован канал с номером 0.   Номер канала может быть от 0 до F - 0 1 2 3 4 5 6 7 8 9 A B C D E или F.
  45 - номер клавиши Ля ( №69 в десятичном представлении)
  7F - самое большое число для обозначения громкости, 01 - самое маленькое. При 00 никакого звука не будет, поэтому сообщение 90 45 00 можно было бы использовать для обрывания звука, но правильнее использовать сообщение, специально предназначенное для этого, с другим первым байтом - 80 45 7F
  Посмотрим на следующую группу сообщений:

  00 C0 00       / в канале 0 назначается инструмент 00 - это рояль
  00 90 45 40    / в канале 0 взять ноту 45 (Ля) средне громко
  81 60 80 45 40 / в канале 0 отпустить ноту 45, параметр 40 хоть и должен означать динамику этого самого отпускания, но чаще всего он значения не имеет - нота Ля просто прерывает своё звучание. Выставляя сообщения друг за другом, можно взять несколько разных нот, создав аккорд. Тогда сообщение "прервать звучание конкретной ноты аккорда" прервёт именно её звучание, а звук других нот останется. Чтобы прервать звучание всех нот на канале 00 нужно обратиться к нему специальным образом - B0 7B 00, здесь 7B - вид того действия, которое мы хотим совершить на канале.
  7B - выключение звука всех нот, 01 - глубина модуляции звука, 07 - громкость канала, 79 - инициализация канала и другие, выполняющиеся в разных стандартах МИДИ. Кое-что делать необязательно, например, специально регулировать громкость канала или инициализировать его. Обращение B0 00 ... заставит поискать банк данных, но если на банк семплов Ваш проигрыватель не рассчитан, то это обращение он проигнорирует.
 
  Каждое сообщение показанной выше группы предваряется обязательным указанием на задержку времени перед выполнением того события, на которое указывает сообщение. Время указывается в специальных единицах - "тиках". Обычно "тик" в несколько сотен раз меньше одной секунды.
  00 означает нулевую задержку во времени - не прошло и тика, как следующее событие уже наступает. Но, давайте, посмотрим на то, что предваряет последнее сообщение - перед ним мы видим два байта - 81 60. Это так называемое число переменной длины.
 
  Первый байт этого число имеет единицу в восьмом разряде, и в самом деле, в двоичном виде он выглядит так:
  81 = 1000 0001  единица в восьмом разряде численного значения не имеет, она особый знак, отмечающий, что этим байтом число переменной длины не заканчивается. А что же является численным значением? Численное значение содержат остальные разряды этого байта - с первого по седьмой. Таким образом, в нём может быть записано число от нуля до 127.
  Второй байт нашего числа переменной длины:
  60 = 0110 000 = 64+32=96 в десятичном виде. И опять, в нём только семь значащих разрядов. Стало быть, в него может быть помещено число от нуля до 127. А сколько же времени в сумме, в двух байтах у нас "натикало"?
  Умножаем содержимое первого байта на 128 и складываем с содержимом второго байта. Получаем
  1*128+96=224 тика. А сколько же это в секундах?
  Сколько тиков приходится на длительность одной четвертной ноты указывается в шапке МИДИ записи. Если там указано 60=96 в десятичном виде, то, вспоминая, что 1 четверть в музыке стандартно длится половину секунды, находим:
  1 тик =.5/96=.00520833 сек  Умножая эту величину на 224 находим 1.166 - время задержки "дельта" (так ещё называется эта величина) составляет чуть больше времени звучания половинной ноты. Столько времени будет звучать наша нота Ля.

  Таким образом, алгоритм чтения МИДИ текста основывается на строгом чередовании времени "дельта" и сообщений. Время "дельта" нельзя указывать в тексте два раза подряд, иначе возникнет путаница в интерпретации читаемых компьютером байтов.

  Как узнать в записи байтов МИДИ начало сообщения?
  На начало сообщения всегда указывает единица в старшем разряде байта. Таким образом, байты, начинающиеся с 16-ти ричных 8 9 A B C D и F, скорее всего будут начальными байтами сообщения. Такие байты называются статусными.
  Сообщение, начинающееся с F, является системным - оно относится не к одному каналу, а целиком к системе, интерпретирующей МИДИ, вызывая её настройку.
  Если сообщение не является системным, то оно называется канальным. Вслед за первым числом в статусном байте канального сообщения идёт второе число, указывающее на номер канала.
  Прекрасно, мы с этим разобрались, но ведь в тексте МИДИ встречаются и другие байты, имеющие единицу в старшем разряде - мы только что о них говорили, они входят в последовательность, изображающую числа переменной длины. Как бы это всё не перепутать?
  Вы правы, но в этом всё же можно разобраться, имея в виду строгую очерёдность появления байтов, составляющих "дельта", и байтов, входящих в сообщение.
 

   СКАЧАЕМ МИДИ ТЕКСТ И ПОСМОТРИМ НА НЕГО

  Просто так, в Блокнотике с МИДИ форматом не разберёшься. Блокнотик показывает только символы, а нужны ещё и коды чисел, находящихся в байтах записи. Небольшая программка написанная на Турбо Бейсике (см. Приложение) справляется с этим. Файл записи в МИДИ формате нужно переименовать в файл MUZ.mid и запустить программу MUZ_T.bas. Поработав несколько секунд программа создаёт два текстовых файла.
  Первый файл T.txt демонстрирует содержимое байтов музыкального файла в столбик (порядковый номер байта и его содержимое: шестнадцатиричное, десятичное число, символ), а в файле T16.txt шестнадцатиричное содержимое представлено в виде строк по 10 байт в каждой - так, как это показано ниже.
 
  На сайте MIDI.RU я скачал аранжировку песни "Сахар не песок" в миди формате, и Вы можете посмотреть на начало расшифровки этого текста -   

 0  4D  77 M    Здесь расшифровка идёт в столбик. МИДИ текст начинается
 1  54  84 T    с шапки, обязательной для всех файлов МИДИ формата.
 2  68  104 h   Надпись MThr символами, и за ней 4 байта, в которых 
 3  64  100 d   указано 6 - расстояние до начала трека 7+6=13
 4  0  0        Трек начинается с байта 14. Но прежде, в байтах 8,9
 5  0  0        указывается формат записи 0 - что означает, что
 6  0  0        все звуки сведены в одну дорожку.
 7  6  6 
 8  0  0       В байтах 10, 11 указано 1 - число дорожек ( для формата 0
 9  0  0       всегда 1) больше одной их может быть только в формате 1
 10  0  0 
 11  1  1      В байтах 12, 13 указано число тиков, приходящихся
 12  0  0       на одну четвертную ноту
 13  78  120 x
 14  4D  77 M    Тут начало трека. За надписью MTrk четыре байта содержат
 15  54  84 T    длину трека - 205*256+255=52735 байт
 16  72  114 r   21+52735 указывает на последний байт трека
 17  6B  107 k   и этим байтом весь файл заканчивается.
 18  0  0 
 19  0  0 
 20  CD  205 Н
 21  FF  255 я   
 22  0  0 
 23  FF  255 я   байты FF вводят так называемое мета сообщение. Оно
 24  3  3       в воспроизводстве звука не участвует, но программе может
 25  E  14       нечто сообщать. В данном случае FF 03 0E сообщает, что
 26  53  83 S     в последующих 14-ти байтах идёт название композиции
 27  61  97 a
 28  68  104 h
 29  6F  111 o
 30  72  114 r    Каким файл MUZ.mid предстаёт в T16.txt показано ниже

4D 54 68 64 0 0 0 6 0 0 / 0       обратите внимание - слежа / означает
0 1 0 78 4D 54 72 6B 0 0 / 10     комментарии. Здесь в качестве комментариев
CD FF 0 FF 3 E 53 61 68 6F / 20     даются номера начальных байтов строк
72 20 6E 65 20 70 65 73 6F 6B / 30
0 FF 3 5 53 76 65 74 61 0 / 40
FF 2 E 57 6C 61 64 69 6D 69 / 50  Такая форма записи даёт возможность
72 20 4E 65 70 6B 65 0 FF 1 / 60  обратной конверсии текста в муз.запись.
1 A 0 F0 5 7E 7F 9 1 F7 / 70       Этот текст нужно переписать в файл MT.txt
0 FF 58 4 4 2 18 8 0 FF / 80       дополнить его строкой из трёх запятых
59 2 0 0 0 FF 51 3 7 A / 90        и обратиться к программе MT_MUZ.bas
E2 78 B0 0 0 1 20 0 1 C0 / 100     Она переформатирует этот текст
46 1 B0 7 7C 0 A 40 2 B1 / 110     в исходный файл MUZ.mid
0 0 1 20 6 1 C1 27 1 B1 / 120
7 70 0 A 40 2 B2 0 0 1 / 130 ...


   СДЕЛАЕМ СВОЙ МИДИ МУЗЫКАЛЬНЫЙ ФАЙЛ

  Так же, как только что было сказано, мы можем поступить с текстом. показанном ниже. То есть, скопировать и записать его в файл MT.txt (вместе со всеми комментариями, только три запятые в конце не забудьте) и выполнить программу MT_MUZ.bas (текст программы приведён в приложении). В результате работы программы получится вот этот файл - http://yadi.sk/d/fbps4XGA3KU4od
  Если на Вашем компьютере имеется МИДИ проигрыватель (на своём ноутбуке я такой проигрыватель только что обнаружил - он встроен в систему Виндос-8. Потому у меня и появился интерес к МИДИ формату), - если это так, то Вы можете проиграть полученный файл MUZ.mid и оценить звучание нот.

4D 54 68 64   / заголовок MThd
00 00 00 06   / длина блока 6 байт
00 00         / формат файла 0
00 01         / один трек
00 60         / 96 тиков в четверти
4D 54 72 6B   / трек MTrk
00 00 00 67             / длина 103 байт
00 FF 58 04 04 02 18 08 / dt=0, музыкальный размер 4/4
              / один удар метронома на четверть,
              / восемь 32-х нот на 24 MIDI Clock   

00 FF 51 03 09 F3 8E / dt=0, темп 652174 мксек на четверть
              / до этого указания темп .5 сек/четверть
00 C0 02      / канал 0 инструмент 2 электропианино
00 C1 00      / канал 1 инструмент 0 рояль
00 C2 29      / канал 2 инструмент 41 альт
70 B0 40 7F   / педаль нажать
00 90 53 60   / dt=0, взять До мал.окт. на канале 2
10 80 53 00   / ноту отпускаем, но она звучит на педали

81 60 B0 40 0 / dt=96 тик, педаль отпустить
00 91 3C 7F   / dt=0, взять До 1-й окт. на 1
08 43 7F      / dt=8, взять Соль 1-й октавы на 1
              / подразумевается, что команда та же
              / то есть 91 - режим Running Status
60 91 3C 7F   / dt=96 тик, взять До 1-й окт. на 1
08 43 7F      / dt=8, взять Соль 1-й октавы на 1
60 91 3C 7F   / dt=96 тик, взять До 1-й окт. на 1
08 43 7F      / dt=8, взять Соль 1-й октавы на 1
08 40 7F
08 42 7F
08 48 7F
08 91 49 7F / большой аккорд, все ли ноты слышны?
 /00 B1 7B 0  / выключить все ноты на этом канале

00 B2 1 40    / модуляция, 11-экспрессия похожа
60 92 4C 60   / dt=96, взять Ми 2-й окт. на канале 2
81 40 82 30 40 /dt=128+64=192 тика - время пременной
               / длины, снять До мал.окт на канале 2
00 81 43 40    / снять Соль 1-й окт. на канале 1
00 3C 40       / и снять До 1-й октавы тоже (RS режим)
00 80 4C 40    / снять Ми 2-й окт. на 0
00 FF 2F 00    / конец трека
,,,


  Вы можете также, насобирав литературы по МИДИ формату, экспериментировать с текстом MT.txt как угодно, прослушивая результат, и выясняя непонятные Вам нюансы. Можете менять текст как угодно, длину его тоже можете менять - программа конверсии сама определит длину трека и запишет её значение в байты 18-21, следующие за заголовком MTrk.
  На рисунке к этой статье показаны частоты и номера клавиш. Номер клавиши можно определить по частоте, округлив N, вычисленное по следующей формуле -

  N = ln(F/440)*17.31234 + 69       здесь 17.31234 это 12/ln(2)

_______

Пример. Расшифровка последовательности МИДИ с 5-ю треками, скачать можно тут -
 http://yadi.sk/d/GbZZnCvXd_Z8eg
 ===============================
   ДОПОЛНЕНИЕ
  В настоящее время тексты программ на языке Турбо Бейсик, которые приводятся ниже, переведены в среду Визуал Бейсик и реализованы в техническом расширении программы Конвертор в МИДИ. О работе этой программы можно прочесть тут -
http://www.proza.ru/2017/10/22/168
  Чтобы попасть в техническое расширение этой программы, нужно кликнуть на отогнутом уголке окна в нижнем правом его углу. После этого будут доступны все операции, описанные выше, и не только они.

 ===============================
   ДОПОЛНЕНИЕ 2  Пример расшифровки МИДИ последовательностей

Редактор МИДИ для звука До выдаёт такую последовательность байт:
90 3C 3D 18 80 3C 00 /взятие (90) и отпускание (80) клавиши До (3C)
интервал (18)=1*16+8=24дес. тиков
Как узнать, что это интервал? Да просто команда 90 3C 3D закончилась, а после
окончания команды ничего кроме интервала идти не может. С нажатиями и отпусканиями клавиш просто.
Вот с обращениями к контроллерам B0 хуже - не всегда понятно,
что они значат и где они кончаются.

Синтезатор Ямаха, записала бы звук ноты До так:
90 3C 3D 18 3C 00 / то есть байт (80) - отпускание клавиши отсутствует, интервал тот же (18)
Следующий за интервалом байт (3C) не является статусным,
то есть он не содержит 1 в старшем разряде. Поэтому, если перед этим обрабатывалось
нажатие клавиши, то нажатие клавиши подразумевается,
и байты (3C 00) обрабатываются, как байты последовательности 90 3C 00
то есть, как нажатие клавиши До с нулевой громкостью звука.

Теперь об интервалах. Перед каждым МИДИ событием указывается интервал времени, прошедшего после начала предыдущего события.
Если число тиков от 0 до 127, то для этого используется один байт, содержащий это число.
Но пусть число тиков 130=128+2. тогда используем 2 байта. (81 02). Если число тиков 259, то представляем это число как 128*2+3 и записываем (82 03)
Каждый раз, чтобы показать, что байт, указывающий на интервал, не последний, мы устанавливаем его старший разряд в единицу.
Вот откуда число 8 появляется. Последовательность. указывающая на интервал может быть и такой
(81 90 05)=1*128*128+(90-80)*128+5    где (90-80)=(10)=16десятичное
Такое представление чисел в МИДИ называется числом переменной длины.
Это вносит в чтение дампа трудности, потому что 90 можно принять за нажатие клавиши на нулевом канале, и интерпретировать следующие байты неверно.

 ===============================
   ПРИЛОЖЕНИЕ  ТЕКСТЫ ПРОГРАММ

'MUZ_T.bas => read content of "MUZ.mid" to "T.txt"
OPEN "b",#1,"MUZ.mid"
OPEN "T.txt" FOR OUTPUT AS #2
OPEN "T16.txt" FOR OUTPUT AS #3
SEEK #1,0 : K=0
FOR I=0 TO LOF(1)-1 ' или конкретно: TO 160
  GET$ #1,1,A$ : N=ASC(A$)
  PRINT #2,I;" ";HEX$(N);" "+STR$(N)+" ";A$
  PRINT #3,HEX$(N);" "; : K=K+1
  IF K=10 THEN K=0 : PRINT #3,"/";I-9
NEXT I : CLOSE #1,#2,#3
PRINT "T and T16 are ready" : END
 =========

'MT_MUZ.bas creaded MUZ.mid file from MT hex
  PRINT "====="
  OPEN "MT.txt" FOR INPUT AS #1
  OPEN "B",#2,"MUZ.mid"
  SEEK #2,0
10 LINE INPUT #1,S$
  IF LEFT$(S$,3)=",,," THEN
   A#=LOC(2)-22 
   AA#=INT(A#/256) : A1=(A#-AA#*256) : A#=AA#
   AA#=INT(A#/256) : A2=(A#-AA#*256) : A#=AA#
   AA#=INT(A#/256) : A3=(A#-AA#*256) : A4=AA#
   PRINT A4;A3;A2;A1; : SEEK #2,18
   B$=CHR$(A4)+CHR$(A3)+CHR$(A2)+CHR$(A1)
   PUT$ #2,B$ : PRINT " Ok" : CLOSE #1,#2 : STOP
  END IF
  IF S$="" THEN GOTO 10
  B$="" : Fc=0 : IM=LEN(S$) 
  FOR I=1 TO IM
   A$=MID$(S$,I,1) : C=FNCOD(A$)
   IF Fc=1 THEN
     IF C>0 THEN C1=C-1 ELSE B$=B$+CHR$(C1) : Fc=0
   END IF
   IF Fc=2 THEN C2=C-1 : B$=B$+CHR$(C1*16+C2) : Fc=0
   IF A$="/" THEN I=IM 'then follows the comment field
   IF I=IM AND Fc=1 THEN B$=B$+CHR$(C1)
  NEXT I
  IF B$="" THEN 10
  PUT$ #2,B$ : GOTO 10
  '===
DEF FNCOD(A$)
LOCAL P
  P=ASC(A$) : IF P<71 AND P>64 THEN P=P-7
  IF P<64 AND P>47 THEN P=P-47 : Fc=Fc+1 : ELSE P=0   
  FNCOD=P
END DEF

================ 16.03.2017


Рецензии