123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- <?php
- class getid3_midi
- {
- function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) {
-
- $ThisFileInfo['midi']['raw'] = array();
- $thisfile_midi = &$ThisFileInfo['midi'];
- $thisfile_midi_raw = &$thisfile_midi['raw'];
- $ThisFileInfo['fileformat'] = 'midi';
- $ThisFileInfo['audio']['dataformat'] = 'midi';
- fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
- $MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
- $offset = 0;
- $MIDIheaderID = substr($MIDIdata, $offset, 4);
- if ($MIDIheaderID != 'MThd') {
- $ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"';
- unset($ThisFileInfo['fileformat']);
- return false;
- }
- $offset += 4;
- $thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
- $offset += 4;
- $thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
- $offset += 2;
- $thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
- $offset += 2;
- $thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
- $offset += 2;
- for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
- if ((strlen($MIDIdata) - $offset) < 8) {
- $MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
- }
- $trackID = substr($MIDIdata, $offset, 4);
- $offset += 4;
- if ($trackID == 'MTrk') {
- $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
- $offset += 4;
-
- $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
- $offset += $tracksize;
- } else {
- $ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead';
- return false;
- }
- }
- if (!isset($trackdataarray) || !is_array($trackdataarray)) {
- $ThisFileInfo['error'][] = 'Cannot find MIDI track information';
- unset($thisfile_midi);
- unset($ThisFileInfo['fileformat']);
- return false;
- }
- if ($scanwholefile) {
- $thisfile_midi['totalticks'] = 0;
- $ThisFileInfo['playtime_seconds'] = 0;
- $CurrentMicroSecondsPerBeat = 500000;
- $CurrentBeatsPerMinute = 120;
- $MicroSecondsPerQuarterNoteAfter = array ();
- foreach ($trackdataarray as $tracknumber => $trackdata) {
- $eventsoffset = 0;
- $LastIssuedMIDIcommand = 0;
- $LastIssuedMIDIchannel = 0;
- $CumulativeDeltaTime = 0;
- $TicksAtCurrentBPM = 0;
- while ($eventsoffset < strlen($trackdata)) {
- $eventid = 0;
- if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
- $eventid = count($MIDIevents[$tracknumber]);
- }
- $deltatime = 0;
- for ($i = 0; $i < 4; $i++) {
- $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
- $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
- if ($deltatimebyte & 0x80) {
-
- } else {
- break;
- }
- }
- $CumulativeDeltaTime += $deltatime;
- $TicksAtCurrentBPM += $deltatime;
- $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
- $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
- if ($MIDI_event_channel & 0x80) {
-
- $LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
- $LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
- } else {
-
- $eventsoffset--;
- }
- $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
- $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
- if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) {
- $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
- $velocity = ord(substr($trackdata, $eventsoffset++, 1));
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) {
- $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
- $velocity = ord(substr($trackdata, $eventsoffset++, 1));
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) {
- $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
- $velocity = ord(substr($trackdata, $eventsoffset++, 1));
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) {
- $controllernum = ord(substr($trackdata, $eventsoffset++, 1));
- $newvalue = ord(substr($trackdata, $eventsoffset++, 1));
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) {
- $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
- $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
- if ($tracknumber == 10) {
- $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
- } else {
- $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
- }
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) {
- $channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
- } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) {
- $changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
- $changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
- $pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
- } elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
- $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
- $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
- $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
- $eventsoffset += $METAeventLength;
- switch ($METAeventCommand) {
- case 0x00:
- $track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
-
- break;
- case 0x01:
- $text_generic = substr($METAeventData, 0, $METAeventLength);
-
- $thisfile_midi['comments']['comment'][] = $text_generic;
- break;
- case 0x02:
- $text_copyright = substr($METAeventData, 0, $METAeventLength);
-
- $thisfile_midi['comments']['copyright'][] = $text_copyright;
- break;
- case 0x03:
- $text_trackname = substr($METAeventData, 0, $METAeventLength);
- $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
- break;
- case 0x04:
- $text_instrument = substr($METAeventData, 0, $METAeventLength);
-
- break;
- case 0x05:
- $text_lyrics = substr($METAeventData, 0, $METAeventLength);
-
- if (!isset($thisfile_midi['lyrics'])) {
- $thisfile_midi['lyrics'] = '';
- }
- $thisfile_midi['lyrics'] .= $text_lyrics."\n";
- break;
- case 0x06:
- $text_marker = substr($METAeventData, 0, $METAeventLength);
-
- break;
- case 0x07:
- $text_cuepoint = substr($METAeventData, 0, $METAeventLength);
-
- break;
- case 0x2F:
-
- break;
- case 0x51:
- $CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
- if ($CurrentMicroSecondsPerBeat == 0) {
- $ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
- return false;
- }
- $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
- $CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
- $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
- $TicksAtCurrentBPM = 0;
- break;
- case 0x58:
- $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
- $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1}));
- $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2});
-
-
-
-
- $thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
- break;
- case 0x59:
- $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
- if ($keysig_sharpsflats & 0x80) {
-
- $keysig_sharpsflats -= 256;
- }
- $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1});
- $keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
-
-
-
-
-
- $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
- break;
- case 0x7F:
- $custom_data = substr($METAeventData, 0, $METAeventLength);
- break;
- default:
- $ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand;
- break;
- }
- } else {
- $ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel'];
- }
- }
- if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
- $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
- }
- }
- $previoustickoffset = null;
- ksort($MicroSecondsPerQuarterNoteAfter);
- foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
- if (is_null($previoustickoffset)) {
- $prevmicrosecondsperbeat = $microsecondsperbeat;
- $previoustickoffset = $tickoffset;
- continue;
- }
- if ($thisfile_midi['totalticks'] > $tickoffset) {
- if ($thisfile_midi_raw['ticksperqnote'] == 0) {
- $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
- return false;
- }
- $ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
- $prevmicrosecondsperbeat = $microsecondsperbeat;
- $previoustickoffset = $tickoffset;
- }
- }
- if ($thisfile_midi['totalticks'] > $previoustickoffset) {
- if ($thisfile_midi_raw['ticksperqnote'] == 0) {
- $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
- return false;
- }
- $ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000);
- }
- }
-
- if (@$ThisFileInfo['playtime_seconds'] > 0) {
- $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
- }
- if (!empty($thisfile_midi['lyrics'])) {
- $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
- }
- return true;
- }
- function GeneralMIDIinstrumentLookup($instrumentid) {
- $begin = __LINE__;
-
- return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
- }
- function GeneralMIDIpercussionLookup($instrumentid) {
- $begin = __LINE__;
-
- return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
- }
- }
- ?>
|