Предисловие
Очень часто сталкиваюсь с тем, что многие не воспринимают 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 файл и открыв его в проигрователе я услышал записанную музыку
А теперь о программировании
Ну если нужно выдрать одну песню из такого файла — то ладно, можно было бы и закончить, но если есть необходимость обрабатывать несколько файлов подобным образом, что тогда? А вот тогда на помощь и приходит автоматизация.
Все действия, которые мы должны выполнить над испытуемым мы выяснили, теперь соберем их воедино и воплотим в жизнь, т.е. в код:
- Открыть файл
- Считать файл
- Перевсти полученный результат в HEX
- Найти совпадения с MTrk-заголовком, если таковые имеются продолжить, иначе — выход
- Найти совпадение с FF 2F 00
- Полученными индексами скопировать подстроку в переменную
- Создать выходной файл
- Записать в него заголовок MThd и переменную с блоком MTrk, обработанных функцией HEX2BIN
- Сохранить, закрыть файл
- Вернуться к п.4
Готовый php-код:
- <?php
- function hex2bin($h) // функция, которой по неизвестной причине нет в php
- {
- if (!is_string($h)) return null;
- $r='';
- for ($a=0; $a<strlen($h); $a+=2) { $r.=chr(hexdec($h{$a}.$h{($a+1)})); }
- return $r;
- }
-
- $lp = 0;
- $i = 0;
- $fh = fopen('test.bup', "r") or die("Can't open file!"); //открываем файл
- $file = fread($fh, filesize('test.bup')); //читаем
- $bin = bin2hex($file); //переводим в hex
-
- while(strpos($bin,'4d54726b',$lp)!==false) //рабочий цикл
- {
- $fp=strpos($bin,'4d54726b',$lp);
- $lp=strpos($bin,'ff2f',$fp);
-
- $getss = substr($bin, $fp,$lp-$fp+6);
-
- $fm = fopen("mid$i.mid","w");
- fwrite($fm, hex2bin('4d54686400000006000000010060'.$getss)); //запись в файл заголовка MThd и MTrk
- fclose($fm);
- $i++;
- }
- fclose($fh);
- ?>
Вот так вот все легко ;)
UPD. По совету неизвестного мне(кроме ника eDogs) пользователя добавлю по поводу функции конвертации в бинарный вид. Вместо нее нее можно использовать стандартную функцию PHP:
string pack ( string $format [, mixed $args [, mixed $... ]] ) )