17 define(
'GETID3_MIDI_MAGIC_MTHD',
'MThd');
18 define(
'GETID3_MIDI_MAGIC_MTRK',
'MTrk');
25 $info = &$this->getid3->info;
29 $thisfile_midi = &
$info[
'midi'];
30 $thisfile_midi_raw = &$thisfile_midi[
'raw'];
32 $info[
'fileformat'] =
'midi';
33 $info[
'audio'][
'dataformat'] =
'midi';
36 $MIDIdata = $this->
fread($this->getid3->fread_buffer_size());
38 $MIDIheaderID = substr($MIDIdata, $offset, 4);
41 unset(
$info[
'fileformat']);
54 for (
$i = 0;
$i < $thisfile_midi_raw[
'tracks'];
$i++) {
55 while ((strlen($MIDIdata) - $offset) < 8) {
56 if ($buffer = $this->
fread($this->getid3->fread_buffer_size())) {
59 $this->
warning(
'only processed '.(
$i - 1).
' of '.$thisfile_midi_raw[
'tracks'].
' tracks');
60 $this->
error(
'Unabled to read more file data at '.$this->
ftell().
' (trying to seek to : '.$offset.
'), was expecting at least 8 more bytes');
64 $trackID = substr($MIDIdata, $offset, 4);
70 $trackdataarray[
$i] = substr($MIDIdata, $offset, $tracksize);
71 $offset += $tracksize;
78 if (!isset($trackdataarray) || !is_array($trackdataarray)) {
79 $this->
error(
'Cannot find MIDI track information');
80 unset($thisfile_midi);
81 unset(
$info[
'fileformat']);
85 if ($this->scanwholefile) {
86 $thisfile_midi[
'totalticks'] = 0;
87 $info[
'playtime_seconds'] = 0;
88 $CurrentMicroSecondsPerBeat = 500000;
89 $CurrentBeatsPerMinute = 120;
90 $MicroSecondsPerQuarterNoteAfter =
array ();
92 foreach ($trackdataarray as $tracknumber => $trackdata) {
95 $LastIssuedMIDIcommand = 0;
96 $LastIssuedMIDIchannel = 0;
97 $CumulativeDeltaTime = 0;
98 $TicksAtCurrentBPM = 0;
99 while ($eventsoffset < strlen($trackdata)) {
101 if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
102 $eventid = count($MIDIevents[$tracknumber]);
105 for (
$i = 0;
$i < 4;
$i++) {
106 $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
107 $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
108 if ($deltatimebyte & 0x80) {
114 $CumulativeDeltaTime += $deltatime;
115 $TicksAtCurrentBPM += $deltatime;
116 $MIDIevents[$tracknumber][$eventid][
'deltatime'] = $deltatime;
117 $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
118 if ($MIDI_event_channel & 0x80) {
120 $LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
121 $LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
126 $MIDIevents[$tracknumber][$eventid][
'eventid'] = $LastIssuedMIDIcommand;
127 $MIDIevents[$tracknumber][$eventid][
'channel'] = $LastIssuedMIDIchannel;
128 if ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x08) {
130 $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
131 $velocity = ord(substr($trackdata, $eventsoffset++, 1));
133 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x09) {
135 $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
136 $velocity = ord(substr($trackdata, $eventsoffset++, 1));
138 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0A) {
140 $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
141 $velocity = ord(substr($trackdata, $eventsoffset++, 1));
143 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0B) {
145 $controllernum = ord(substr($trackdata, $eventsoffset++, 1));
146 $newvalue = ord(substr($trackdata, $eventsoffset++, 1));
148 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0C) {
150 $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
152 $thisfile_midi_raw[
'track'][$tracknumber][
'instrumentid'] = $newprogramnum;
153 if ($tracknumber == 10) {
159 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0D) {
161 $channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
163 } elseif ($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0E) {
165 $changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
166 $changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
167 $pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
169 } elseif (($MIDIevents[$tracknumber][$eventid][
'eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid][
'channel'] == 0x0F)) {
171 $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
172 $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
173 $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
174 $eventsoffset += $METAeventLength;
175 switch ($METAeventCommand) {
182 $text_generic = substr($METAeventData, 0, $METAeventLength);
184 $thisfile_midi[
'comments'][
'comment'][] = $text_generic;
188 $text_copyright = substr($METAeventData, 0, $METAeventLength);
190 $thisfile_midi[
'comments'][
'copyright'][] = $text_copyright;
194 $text_trackname = substr($METAeventData, 0, $METAeventLength);
195 $thisfile_midi_raw[
'track'][$tracknumber][
'name'] = $text_trackname;
199 $text_instrument = substr($METAeventData, 0, $METAeventLength);
204 $text_lyrics = substr($METAeventData, 0, $METAeventLength);
206 if (!isset($thisfile_midi[
'lyrics'])) {
207 $thisfile_midi[
'lyrics'] =
'';
209 $thisfile_midi[
'lyrics'] .= $text_lyrics.
"\n";
213 $text_marker = substr($METAeventData, 0, $METAeventLength);
218 $text_cuepoint = substr($METAeventData, 0, $METAeventLength);
228 if ($CurrentMicroSecondsPerBeat == 0) {
229 $this->
error(
'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero');
232 $thisfile_midi_raw[
'events'][$tracknumber][$CumulativeDeltaTime][
'us_qnote'] = $CurrentMicroSecondsPerBeat;
233 $CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
234 $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
235 $TicksAtCurrentBPM = 0;
246 $thisfile_midi[
'timesignature'][] = $timesig_numerator.
'/'.$timesig_denominator;
251 if ($keysig_sharpsflats & 0x80) {
253 $keysig_sharpsflats -= 256;
257 $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#');
264 $thisfile_midi[
'keysignature'][] = $keysigs[$keysig_sharpsflats].
' '.((bool) $keysig_majorminor ?
'minor' :
'major');
268 $custom_data = substr($METAeventData, 0, $METAeventLength);
272 $this->
warning(
'Unhandled META Event Command: '.$METAeventCommand);
278 $this->
warning(
'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid][
'eventid'].
' + Channel ID: '.$MIDIevents[$tracknumber][$eventid][
'channel']);
282 if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
283 $thisfile_midi[
'totalticks'] = max($thisfile_midi[
'totalticks'], $CumulativeDeltaTime);
286 $previoustickoffset = null;
288 ksort($MicroSecondsPerQuarterNoteAfter);
289 foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
290 if (is_null($previoustickoffset)) {
291 $prevmicrosecondsperbeat = $microsecondsperbeat;
292 $previoustickoffset = $tickoffset;
295 if ($thisfile_midi[
'totalticks'] > $tickoffset) {
297 if ($thisfile_midi_raw[
'ticksperqnote'] == 0) {
298 $this->
error(
'Corrupt MIDI file: ticksperqnote == zero');
302 $info[
'playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw[
'ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
304 $prevmicrosecondsperbeat = $microsecondsperbeat;
305 $previoustickoffset = $tickoffset;
308 if ($thisfile_midi[
'totalticks'] > $previoustickoffset) {
310 if ($thisfile_midi_raw[
'ticksperqnote'] == 0) {
311 $this->
error(
'Corrupt MIDI file: ticksperqnote == zero');
315 $info[
'playtime_seconds'] += (($thisfile_midi[
'totalticks'] - $previoustickoffset) / $thisfile_midi_raw[
'ticksperqnote']) * ($microsecondsperbeat / 1000000);
321 if (!empty(
$info[
'playtime_seconds'])) {
322 $info[
'bitrate'] = ((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) /
$info[
'playtime_seconds'];
325 if (!empty($thisfile_midi[
'lyrics'])) {
326 $thisfile_midi[
'comments'][
'lyrics'][] = $thisfile_midi[
'lyrics'];
static EmbeddedLookup($key, $begin, $end, $file, $name)
const GETID3_MIDI_MAGIC_MTRK
GeneralMIDIinstrumentLookup($instrumentid)
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
GeneralMIDIpercussionLookup($instrumentid)
Create styles array
The data for the language used.
fseek($bytes, $whence=SEEK_SET)
const GETID3_MIDI_MAGIC_MTHD
getID3() by James Heinrich info@getid3.org //
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)