function parse($mfidata) { $reader = new IO_Bit(); $reader->input($mfidata); // Header Part $identifier = $reader->getData(4); if ($identifier != 'melo') { throw new Exception('Identifer($identifier) is not melo'); } $this->headers['Identifier'] = $identifier; $fileLength = $reader->getUI32BE(); $realFileLength = strlen($mfidata); if ($fileLength != $realFileLength - 8) { throw new Exception("FileLength({$fileLength}) is not real FileLength({$realFileLength}) - 8"); } $this->headers['FileLength'] = $fileLength; $offsetToTrack = $reader->getUI16BE(); $this->headers['OffsetToTrack'] = $offsetToTrack; $this->headers['DataTypeMajor'] = $reader->getUI8(); $this->headers['DataTypeMinor'] = $reader->getUI8(); $this->headers['NumberOfTrack'] = $reader->getUI8(); // Data Information Part $reader_di = new IO_Bit(); $reader_di->input($reader->getData($offsetToTrack - 3)); $note_message_length_info = 0; while ($reader_di->hasNextData(6)) { $info = array(); $identifer = $reader_di->getData(4); $length = $reader_di->getUI16BE(); $info['Identifer'] = $identifer; $info['Length'] = $length; switch ($identifer) { case 'titl': case 'copy': case 'prot': case 'date': $data = $reader_di->getData($length); break; case 'sorc': $data = $reader_di->getUI8($length); break; case 'note': case 'exst': $data = $reader_di->getUI16BE($length); if ($identifer == 'note') { $note_message_length_info = $data; } break; case 'vers': $data = $reader_di->getUI32BE($length); break; } $info['Data'] = $data; $this->datainfos[] = $info; } // Track Part while ($reader->hasNextData(8)) { $track = array(); $track['Identifer'] = $reader->getData(4); $length = $reader->getUI32BE(); $track['Length'] = $length; $reader_ev = new IO_Bit(); $reader_ev->input($reader->getData($length)); $track['Events'] = array(); while ($reader_ev->hasNextData(3)) { $event = array(); $event['DeltaTime'] = $reader_ev->getUI8(); $statusInfo = $reader_ev->getUI8(); $event['StatusInfo'] = $statusInfo; switch ($statusInfo) { default: $event['Data'] = $reader_ev->getUI8(); if ($note_message_length_info == 1) { // MFi2 $event['Data2'] = $reader_ev->getUI8(); } break; case 0xff: $statusInfo2 = $reader_ev->getUI8(); $event['StatusInfo2'] = $statusInfo2; switch ($statusInfo2) { default: $event['Data'] = $reader_ev->getUI8(); break; case 0xf0: case 0xf1: case 0xff: $length = $reader_ev->getUI16BE(); $event['Length'] = $length; $event['Data'] = $reader_ev->getData($length); break; } } $track['Events'][] = $event; } $this->tracks[] = $track; } }
function parseDataChunks(&$reader) { $chunks = array(); while ($reader->hasNextData(8)) { $chunk = array(); $chunkID = $reader->getData(4); $size = $reader->getUI32BE(); $chunk['ID'] = $chunkID; $chunkID_3byte = substr($chunkID, 0, 3); $chunkID_4th_value = ord($chunkID[3]); switch ($chunkID_3byte) { case 'Dch': $chunk['ID'] = $chunkID_3byte; $chunk['CodeType'] = $chunkID_4th_value; break; case 'MTR': case 'ATR': case 'GTR': $chunk['ID'] = $chunkID_3byte; $chunk['TrackNumber'] = $chunkID_4th_value; break; } $chunk['Size'] = $size; switch (substr($chunkID, 0, 3)) { case 'CNT': // CNTI: Contents Information Chunk $chunk['Contents Class'] = $reader->getUI8(); $chunk['Contents Type'] = $reader->getUI8(); $chunk['Contents Code Type'] = $reader->getUI8(); $chunk['Copy Status'] = $reader->getUI8(); $chunk['Copy Counts'] = $reader->getUI8(); if ($size > 5) { $chunk['Option'] = $reader->getData($size - 5); } break; case 'MTR': // MTR*: Score Track Chunk $chunk['Format Type'] = $reader->getUI8(); $chunk['Sequence Type'] = $reader->getUI8(); $chunk['Timebase D'] = $reader->getUI8(); $chunk['Timebase G'] = $reader->getUI8(); $chunk['Channnel Status'] = $reader->getUI16BE(); $data = $reader->getData($size - 6); $reader_chunkdata = new IO_Bit(); $reader_chunkdata->input($data); $this->seekToAlphabet($reader_chunkdata); $chunk[$structure_type] = $this->parseDataChunks($reader_chunkdata); break; case 'Mts': // Mtsu, Mtsq, Mts $data = $reader->getData($size); $reader_tracks = new IO_Bit(); $reader_tracks->input($data); switch ($chunkID) { case 'Mtsu': // Score Track Setup Data // Score Track Setup Data case 'Mtsq': // Score Track Sequence Data $tracks = array(); while ($reader_tracks->hasNextData(4)) { $end_check = $reader_tracks->getUI32BE(); if ($end_check == 0) { break; } $reader_tracks->incrementOffset(-4, 0); $track = array(); // Duration if ($chunkID != 'Mtsu') { // Mtsu は Duration 無し $duration = $reader_tracks->getUI8(); if ($duration & 0x80) { $duration = ($duration & 0x7f) << 8; $duration |= $reader_tracks->getUI8(); } $track['Duration'] = $duration; } // Event $event = $reader_tracks->getUI8(); $eventData = array(); if ($event != 0) { $eventData['Type'] = 'Note Message'; $eventData['GetTime'] = $reader_tracks->getUI8(); // Option ??? } else { $data1 = $reader_tracks->getUI8(); $eventData['Channel'] = $data1 >> 6; if (($data1 & 0xf) == 0) { $eventData['Type'] = 'Program change'; } elseif (($data1 & 0xf) == 1) { $eventData['Type'] = 'Bank Select'; } else { $msg = sprintf("Unknow Event Data#1=0x%02x", $data1); // throw new Exception($msg); printf($msg . "\n"); break 2; } $eventData['Value'] = $reader_tracks->getUI8(); } $track['event'] = $tracks[] = $track; } $chunk['Tracks'] = $tracks; break 2; } default: $data = $reader->getData($size); if ($structure_type = $this->nestedChunkIDSize($chunkID)) { $reader_chunkdata = new IO_Bit(); $reader_chunkdata->input($data); $method = 'parseData' . $structure_type; $chunk[$structure_type] = $this->{$method}($reader_chunkdata); } else { $chunk['Data'] = $data; } break; } // var_dump($chunk); $chunks[] = $chunk; } return $chunks; }