Pull to refresh

О midi-файлах и о PHP, как инструменте повседневного программирования на живом примере

Reading time 3 min
Views 3.1K
PHP *

Предисловие


Очень часто сталкиваюсь с тем, что многие не воспринимают PHP ни как иначе, чем средство для разработки сайтов, но мне хочется разубедить их этим топиком.

Сегодня мне позвонили и попросили помочь с синтезатором фирмы YAMAHA (в частности с аналогом модели 403), который имеет одну интересную особенность — он позволяет сохранять 5 сочиненных песен в своей памяти, но вот достать их на компьютер в какой либо форме, кроме как файла резервной копии нельзя, о чем говорится на всех форумах и в технической документации к этому синтезатору.


Формат .bup используется синтезатором для сохранения всех настроек и 5 записанных песен.

Все советы по записи этих мелодий сводятся к тому, что бы записать музыку с синтезатора в рельаном времени, но ведь это не выход.

Итак начнем



В моих руках оказался файл с именем 08PK61.BUP. Первый взгляд на него не вызвал никаких положительных эмоций:
06PK61 BackFile
Гласил ascii-заголовок файла. Гугл тоже не дал никаких положительных результатов.
Но тут я вспомнил, что синтезаторы используют формат midi для музыки, и почему бы разработчикам не хранить в backup-файле стандартный midi-поток?
«Но как я его найду?!» пронеслось в моей голове. А все оказалось легко.
Как выяснилось, стандартный заголовок midi-файла выглядит так:

4D 54 68 64 00 00 00 06 00 00 00 01 00 60

Для упрощенного понимания я разбил заголовок на 5 частей:
  • Mthd — заголовок, именно с него начинается midi-файл
  • 00 00 00 06 — длинна данных за этим блоком(в байтах)
  • 00 00 — тип файла(3 вида)
  • 00 01 — количество MTrk блоков
  • 00 60 — темп


Ну это я вычитал в Википедии, и тут же решил вбить MThd в поиск по этому файлу. Тут меня ждало разочарование — этой последовательности байт в файле не оказалось. У меня внось опустились руки.

От нечего делать решил дочитать статью до конца(и понял, что это надо делать всегда) и увидел там про записи MTrk(о них кратко):
Начинается с последовательности
4D 54 72 6B
Дельше идут данные и блок заканчивается последовательностью
FF 2F 00

Начинаю поиск по последовательности 4D 54 72 6B, и о чудо, я нашел целых три таких записи.

Дальше дело оставалось за малым:
Необходимо скопировать блок MTrk заканчивающийся последовательностью FF 2F 00 и дополнив вначало стандартным заголовком MThd записать все это в файл.
Сказано — сделано.
Сохранив результат в mid файл и открыв его в проигрователе я услышал записанную музыку

А теперь о программировании



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

Все действия, которые мы должны выполнить над испытуемым мы выяснили, теперь соберем их воедино и воплотим в жизнь, т.е. в код:

  1. Открыть файл
  2. Считать файл
  3. Перевсти полученный результат в HEX
  4. Найти совпадения с MTrk-заголовком, если таковые имеются продолжить, иначе — выход
  5. Найти совпадение с FF 2F 00
  6. Полученными индексами скопировать подстроку в переменную
  7. Создать выходной файл
  8. Записать в него заголовок MThd и переменную с блоком MTrk, обработанных функцией HEX2BIN
  9. Сохранить, закрыть файл
  10. Вернуться к п.4


Готовый php-код:
  1. <?php
  2. function hex2bin($h) // функция, которой по неизвестной причине нет в php
  3. {
  4.     if (!is_string($h)) return null;
  5.     $r='';
  6.     for ($a=0; $a<strlen($h); $a+=2) { $r.=chr(hexdec($h{$a}.$h{($a+1)})); }
  7.     return $r;
  8. }
  9.  
  10. $lp = 0;
  11. $i = 0;
  12. $fh = fopen('test.bup', "r") or die("Can't open file!"); //открываем файл
  13. $file = fread($fh, filesize('test.bup')); //читаем
  14. $bin = bin2hex($file); //переводим в hex
  15.  
  16. while(strpos($bin,'4d54726b',$lp)!==false) //рабочий цикл
  17. {
  18.     $fp=strpos($bin,'4d54726b',$lp);
  19.     $lp=strpos($bin,'ff2f',$fp);
  20.  
  21.     $getss = substr($bin, $fp,$lp-$fp+6);
  22.  
  23.     $fm = fopen("mid$i.mid","w");
  24.     fwrite($fm, hex2bin('4d54686400000006000000010060'.$getss)); //запись в файл заголовка MThd и MTrk
  25.     fclose($fm);
  26.     $i++;
  27. }
  28. fclose($fh);
  29. ?>


Вот так вот все легко ;)

UPD. По совету неизвестного мне(кроме ника eDogs) пользователя добавлю по поводу функции конвертации в бинарный вид. Вместо нее нее можно использовать стандартную функцию PHP:
string pack ( string $format [, mixed $args [, mixed $... ]] ) )
Tags:
Hubs:
Total votes 55: ↑43 and ↓12 +31
Comments 45
Comments Comments 45

Posts