view e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.midi.php @ 25:6a3d04638a89 judyates

mailto links.
author Robert McIntyre <rlm@mit.edu>
date Sat, 19 Jul 2014 14:35:52 -0400
parents 3f6b44aa6b35
children
line wrap: on
line source
1 <?php
2 // +----------------------------------------------------------------------+
3 // | PHP version 5 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2 of the GPL license, |
8 // | that is bundled with this package in the file license.txt and is |
9 // | available through the world-wide-web at the following url: |
10 // | http://www.gnu.org/copyleft/gpl.html |
11 // +----------------------------------------------------------------------+
12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
13 // +----------------------------------------------------------------------+
14 // | Authors: James Heinrich <infoØgetid3*org> |
15 // | Allan Hansen <ahØartemis*dk> |
16 // +----------------------------------------------------------------------+
17 // | module.audio.midi.php |
18 // | Module for analyzing midi audio files |
19 // | dependencies: NONE |
20 // +----------------------------------------------------------------------+
21 //
22 // $Id: module.audio.midi.php,v 1.5 2006/11/02 10:48:01 ah Exp $
26 class getid3_midi extends getid3_handler
27 {
29 public function Analyze() {
31 $getid3 = $this->getid3;
33 $getid3->info['midi']['raw'] = array ();
34 $info_midi = &$getid3->info['midi'];
35 $info_midi_raw = &$info_midi['raw'];
37 $getid3->info['fileformat'] = 'midi';
38 $getid3->info['audio']['dataformat'] = 'midi';
40 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
41 $midi_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
43 // Magic bytes: 'MThd'
45 getid3_lib::ReadSequence('BigEndian2Int', $info_midi_raw, $midi_data, 4,
46 array (
47 'headersize' => 4,
48 'fileformat' => 2,
49 'tracks' => 2,
50 'ticksperqnote' => 2
51 )
52 );
54 $offset = 14;
56 for ($i = 0; $i < $info_midi_raw['tracks']; $i++) {
58 if ((strlen($midi_data) - $offset) < 8) {
59 $midi_data .= fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
60 }
62 $track_id = substr($midi_data, $offset, 4);
63 $offset += 4;
65 if ($track_id != 'MTrk') {
66 throw new getid3_exception('Expecting "MTrk" at '.$offset.', found '.$track_id.' instead');
67 }
69 $track_size = getid3_lib::BigEndian2Int(substr($midi_data, $offset, 4));
70 $offset += 4;
72 $track_data_array[$i] = substr($midi_data, $offset, $track_size);
73 $offset += $track_size;
74 }
76 if (!isset($track_data_array) || !is_array($track_data_array)) {
77 throw new getid3_exception('Cannot find MIDI track information');
78 }
81 $info_midi['totalticks'] = 0;
82 $getid3->info['playtime_seconds'] = 0;
83 $current_ms_per_beat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
84 $current_beats_per_min = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
85 $ms_per_quarter_note_after = array ();
87 foreach ($track_data_array as $track_number => $track_data) {
89 $events_offset = $last_issued_midi_command = $last_issued_midi_channel = $cumulative_delta_time = $ticks_at_current_bpm = 0;
91 while ($events_offset < strlen($track_data)) {
93 $event_id = 0;
94 if (isset($midi_events[$track_number]) && is_array($midi_events[$track_number])) {
95 $event_id = count($midi_events[$track_number]);
96 }
97 $delta_time = 0;
98 for ($i = 0; $i < 4; $i++) {
99 $delta_time_byte = ord($track_data{$events_offset++});
100 $delta_time = ($delta_time << 7) + ($delta_time_byte & 0x7F);
101 if ($delta_time_byte & 0x80) {
102 // another byte follows
103 } else {
104 break;
105 }
106 }
108 $cumulative_delta_time += $delta_time;
109 $ticks_at_current_bpm += $delta_time;
111 $midi_events[$track_number][$event_id]['deltatime'] = $delta_time;
113 $midi_event_channel = ord($track_data{$events_offset++});
115 // OK, normal event - MIDI command has MSB set
116 if ($midi_event_channel & 0x80) {
117 $last_issued_midi_command = $midi_event_channel >> 4;
118 $last_issued_midi_channel = $midi_event_channel & 0x0F;
119 }
121 // Running event - assume last command
122 else {
123 $events_offset--;
124 }
126 $midi_events[$track_number][$event_id]['eventid'] = $last_issued_midi_command;
127 $midi_events[$track_number][$event_id]['channel'] = $last_issued_midi_channel;
129 switch ($midi_events[$track_number][$event_id]['eventid']) {
131 case 0x8: // Note off (key is released)
132 case 0x9: // Note on (key is pressed)
133 case 0xA: // Key after-touch
135 //$notenumber = ord($track_data{$events_offset++});
136 //$velocity = ord($track_data{$events_offset++});
137 $events_offset += 2;
138 break;
141 case 0xB: // Control Change
143 //$controllernum = ord($track_data{$events_offset++});
144 //$newvalue = ord($track_data{$events_offset++});
145 $events_offset += 2;
146 break;
149 case 0xC: // Program (patch) change
151 $new_program_num = ord($track_data{$events_offset++});
153 $info_midi_raw['track'][$track_number]['instrumentid'] = $new_program_num;
154 $info_midi_raw['track'][$track_number]['instrument'] = $track_number == 10 ? getid3_midi::GeneralMIDIpercussionLookup($new_program_num) : getid3_midi::GeneralMIDIinstrumentLookup($new_program_num);
155 break;
158 case 0xD: // Channel after-touch
160 //$channelnumber = ord($track_data{$events_offset++});
161 break;
164 case 0xE: // Pitch wheel change (2000H is normal or no change)
166 //$changeLSB = ord($track_data{$events_offset++});
167 //$changeMSB = ord($track_data{$events_offset++});
168 //$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
169 $events_offset += 2;
170 break;
173 case 0xF:
175 if ($midi_events[$track_number][$event_id]['channel'] == 0xF) {
177 $meta_event_command = ord($track_data{$events_offset++});
178 $meta_event_length = ord($track_data{$events_offset++});
179 $meta_event_data = substr($track_data, $events_offset, $meta_event_length);
180 $events_offset += $meta_event_length;
182 switch ($meta_event_command) {
184 case 0x00: // Set track sequence number
186 //$track_sequence_number = getid3_lib::BigEndian2Int(substr($meta_event_data, 0, $meta_event_length));
187 //$info_midi_raw['events'][$track_number][$event_id]['seqno'] = $track_sequence_number;
188 break;
191 case 0x01: // Text: generic
193 $text_generic = substr($meta_event_data, 0, $meta_event_length);
194 //$info_midi_raw['events'][$track_number][$event_id]['text'] = $text_generic;
195 $info_midi['comments']['comment'][] = $text_generic;
196 break;
199 case 0x02: // Text: copyright
201 $text_copyright = substr($meta_event_data, 0, $meta_event_length);
202 //$info_midi_raw['events'][$track_number][$event_id]['copyright'] = $text_copyright;
203 $info_midi['comments']['copyright'][] = $text_copyright;
204 break;
207 case 0x03: // Text: track name
209 $text_trackname = substr($meta_event_data, 0, $meta_event_length);
210 $info_midi_raw['track'][$track_number]['name'] = $text_trackname;
211 break;
214 case 0x04: // Text: track instrument name
216 //$text_instrument = substr($meta_event_data, 0, $meta_event_length);
217 //$info_midi_raw['events'][$track_number][$event_id]['instrument'] = $text_instrument;
218 break;
221 case 0x05: // Text: lyrics
223 $text_lyrics = substr($meta_event_data, 0, $meta_event_length);
224 //$info_midi_raw['events'][$track_number][$event_id]['lyrics'] = $text_lyrics;
225 if (!isset($info_midi['lyrics'])) {
226 $info_midi['lyrics'] = '';
227 }
228 $info_midi['lyrics'] .= $text_lyrics . "\n";
229 break;
232 case 0x06: // Text: marker
234 //$text_marker = substr($meta_event_data, 0, $meta_event_length);
235 //$info_midi_raw['events'][$track_number][$event_id]['marker'] = $text_marker;
236 break;
239 case 0x07: // Text: cue point
241 //$text_cuepoint = substr($meta_event_data, 0, $meta_event_length);
242 //$info_midi_raw['events'][$track_number][$event_id]['cuepoint'] = $text_cuepoint;
243 break;
246 case 0x2F: // End Of Track
248 //$info_midi_raw['events'][$track_number][$event_id]['EOT'] = $cumulative_delta_time;
249 break;
252 case 0x51: // Tempo: microseconds / quarter note
254 $current_ms_per_beat = getid3_lib::BigEndian2Int(substr($meta_event_data, 0, $meta_event_length));
255 $info_midi_raw['events'][$track_number][$cumulative_delta_time]['us_qnote'] = $current_ms_per_beat;
256 $current_beats_per_min = (1000000 / $current_ms_per_beat) * 60;
257 $ms_per_quarter_note_after[$cumulative_delta_time] = $current_ms_per_beat;
258 $ticks_at_current_bpm = 0;
259 break;
262 case 0x58: // Time signature
263 $timesig_numerator = getid3_lib::BigEndian2Int($meta_event_data[0]);
264 $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($meta_event_data[1])); // $02 -> x/4, $03 -> x/8, etc
265 //$timesig_32inqnote = getid3_lib::BigEndian2Int($meta_event_data[2]); // number of 32nd notes to the quarter note
266 //$info_midi_raw['events'][$track_number][$event_id]['timesig_32inqnote'] = $timesig_32inqnote;
267 //$info_midi_raw['events'][$track_number][$event_id]['timesig_numerator'] = $timesig_numerator;
268 //$info_midi_raw['events'][$track_number][$event_id]['timesig_denominator'] = $timesig_denominator;
269 //$info_midi_raw['events'][$track_number][$event_id]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator;
270 $info_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
271 break;
274 case 0x59: // Keysignature
276 $keysig_sharpsflats = getid3_lib::BigEndian2Int($meta_event_data{0});
277 if ($keysig_sharpsflats & 0x80) {
278 // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
279 $keysig_sharpsflats -= 256;
280 }
282 $keysig_majorminor = getid3_lib::BigEndian2Int($meta_event_data{1}); // 0 -> major, 1 -> minor
283 $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#');
284 //$info_midi_raw['events'][$track_number][$event_id]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
285 //$info_midi_raw['events'][$track_number][$event_id]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
286 //$info_midi_raw['events'][$track_number][$event_id]['keysig_minor'] = (bool)$keysig_majorminor;
287 //$info_midi_raw['events'][$track_number][$event_id]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($info_midi_raw['events'][$track_number][$event_id]['keysig_minor'] ? 'minor' : 'major');
289 // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
290 $info_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool)$keysig_majorminor ? 'minor' : 'major');
291 break;
294 case 0x7F: // Sequencer specific information
296 $custom_data = substr($meta_event_data, 0, $meta_event_length);
297 break;
300 default:
302 $getid3->warning('Unhandled META Event Command: '.$meta_event_command);
303 }
304 }
305 break;
308 default:
309 $getid3->warning('Unhandled MIDI Event ID: '.$midi_events[$track_number][$event_id]['eventid']);
310 }
311 }
313 if (($track_number > 0) || (count($track_data_array) == 1)) {
314 $info_midi['totalticks'] = max($info_midi['totalticks'], $cumulative_delta_time);
315 }
316 }
318 $previous_tick_offset = null;
320 ksort($ms_per_quarter_note_after);
321 foreach ($ms_per_quarter_note_after as $tick_offset => $ms_per_beat) {
323 if (is_null($previous_tick_offset)) {
324 $prev_ms_per_beat = $ms_per_beat;
325 $previous_tick_offset = $tick_offset;
326 continue;
327 }
329 if ($info_midi['totalticks'] > $tick_offset) {
330 $getid3->info['playtime_seconds'] += (($tick_offset - $previous_tick_offset) / $info_midi_raw['ticksperqnote']) * ($prev_ms_per_beat / 1000000);
332 $prev_ms_per_beat = $ms_per_beat;
333 $previous_tick_offset = $tick_offset;
334 }
335 }
337 if ($info_midi['totalticks'] > $previous_tick_offset) {
338 $getid3->info['playtime_seconds'] += (($info_midi['totalticks'] - $previous_tick_offset) / $info_midi_raw['ticksperqnote']) * ($ms_per_beat / 1000000);
339 }
341 if (@$getid3->info['playtime_seconds'] > 0) {
342 $getid3->info['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
343 }
345 if (!empty($info_midi['lyrics'])) {
346 $info_midi['comments']['lyrics'][] = $info_midi['lyrics'];
347 }
349 return true;
350 }
354 public static function GeneralMIDIinstrumentLookup($instrument_id) {
356 static $lookup = array (
358 0 => 'Acoustic Grand',
359 1 => 'Bright Acoustic',
360 2 => 'Electric Grand',
361 3 => 'Honky-Tonk',
362 4 => 'Electric Piano 1',
363 5 => 'Electric Piano 2',
364 6 => 'Harpsichord',
365 7 => 'Clavier',
366 8 => 'Celesta',
367 9 => 'Glockenspiel',
368 10 => 'Music Box',
369 11 => 'Vibraphone',
370 12 => 'Marimba',
371 13 => 'Xylophone',
372 14 => 'Tubular Bells',
373 15 => 'Dulcimer',
374 16 => 'Drawbar Organ',
375 17 => 'Percussive Organ',
376 18 => 'Rock Organ',
377 19 => 'Church Organ',
378 20 => 'Reed Organ',
379 21 => 'Accordian',
380 22 => 'Harmonica',
381 23 => 'Tango Accordian',
382 24 => 'Acoustic Guitar (nylon)',
383 25 => 'Acoustic Guitar (steel)',
384 26 => 'Electric Guitar (jazz)',
385 27 => 'Electric Guitar (clean)',
386 28 => 'Electric Guitar (muted)',
387 29 => 'Overdriven Guitar',
388 30 => 'Distortion Guitar',
389 31 => 'Guitar Harmonics',
390 32 => 'Acoustic Bass',
391 33 => 'Electric Bass (finger)',
392 34 => 'Electric Bass (pick)',
393 35 => 'Fretless Bass',
394 36 => 'Slap Bass 1',
395 37 => 'Slap Bass 2',
396 38 => 'Synth Bass 1',
397 39 => 'Synth Bass 2',
398 40 => 'Violin',
399 41 => 'Viola',
400 42 => 'Cello',
401 43 => 'Contrabass',
402 44 => 'Tremolo Strings',
403 45 => 'Pizzicato Strings',
404 46 => 'Orchestral Strings',
405 47 => 'Timpani',
406 48 => 'String Ensemble 1',
407 49 => 'String Ensemble 2',
408 50 => 'SynthStrings 1',
409 51 => 'SynthStrings 2',
410 52 => 'Choir Aahs',
411 53 => 'Voice Oohs',
412 54 => 'Synth Voice',
413 55 => 'Orchestra Hit',
414 56 => 'Trumpet',
415 57 => 'Trombone',
416 58 => 'Tuba',
417 59 => 'Muted Trumpet',
418 60 => 'French Horn',
419 61 => 'Brass Section',
420 62 => 'SynthBrass 1',
421 63 => 'SynthBrass 2',
422 64 => 'Soprano Sax',
423 65 => 'Alto Sax',
424 66 => 'Tenor Sax',
425 67 => 'Baritone Sax',
426 68 => 'Oboe',
427 69 => 'English Horn',
428 70 => 'Bassoon',
429 71 => 'Clarinet',
430 72 => 'Piccolo',
431 73 => 'Flute',
432 74 => 'Recorder',
433 75 => 'Pan Flute',
434 76 => 'Blown Bottle',
435 77 => 'Shakuhachi',
436 78 => 'Whistle',
437 79 => 'Ocarina',
438 80 => 'Lead 1 (square)',
439 81 => 'Lead 2 (sawtooth)',
440 82 => 'Lead 3 (calliope)',
441 83 => 'Lead 4 (chiff)',
442 84 => 'Lead 5 (charang)',
443 85 => 'Lead 6 (voice)',
444 86 => 'Lead 7 (fifths)',
445 87 => 'Lead 8 (bass + lead)',
446 88 => 'Pad 1 (new age)',
447 89 => 'Pad 2 (warm)',
448 90 => 'Pad 3 (polysynth)',
449 91 => 'Pad 4 (choir)',
450 92 => 'Pad 5 (bowed)',
451 93 => 'Pad 6 (metallic)',
452 94 => 'Pad 7 (halo)',
453 95 => 'Pad 8 (sweep)',
454 96 => 'FX 1 (rain)',
455 97 => 'FX 2 (soundtrack)',
456 98 => 'FX 3 (crystal)',
457 99 => 'FX 4 (atmosphere)',
458 100 => 'FX 5 (brightness)',
459 101 => 'FX 6 (goblins)',
460 102 => 'FX 7 (echoes)',
461 103 => 'FX 8 (sci-fi)',
462 104 => 'Sitar',
463 105 => 'Banjo',
464 106 => 'Shamisen',
465 107 => 'Koto',
466 108 => 'Kalimba',
467 109 => 'Bagpipe',
468 110 => 'Fiddle',
469 111 => 'Shanai',
470 112 => 'Tinkle Bell',
471 113 => 'Agogo',
472 114 => 'Steel Drums',
473 115 => 'Woodblock',
474 116 => 'Taiko Drum',
475 117 => 'Melodic Tom',
476 118 => 'Synth Drum',
477 119 => 'Reverse Cymbal',
478 120 => 'Guitar Fret Noise',
479 121 => 'Breath Noise',
480 122 => 'Seashore',
481 123 => 'Bird Tweet',
482 124 => 'Telephone Ring',
483 125 => 'Helicopter',
484 126 => 'Applause',
485 127 => 'Gunshot'
486 );
488 return @$lookup[$instrument_id];
489 }
493 public static function GeneralMIDIpercussionLookup($instrument_id) {
495 static $lookup = array (
497 35 => 'Acoustic Bass Drum',
498 36 => 'Bass Drum 1',
499 37 => 'Side Stick',
500 38 => 'Acoustic Snare',
501 39 => 'Hand Clap',
502 40 => 'Electric Snare',
503 41 => 'Low Floor Tom',
504 42 => 'Closed Hi-Hat',
505 43 => 'High Floor Tom',
506 44 => 'Pedal Hi-Hat',
507 45 => 'Low Tom',
508 46 => 'Open Hi-Hat',
509 47 => 'Low-Mid Tom',
510 48 => 'Hi-Mid Tom',
511 49 => 'Crash Cymbal 1',
512 50 => 'High Tom',
513 51 => 'Ride Cymbal 1',
514 52 => 'Chinese Cymbal',
515 53 => 'Ride Bell',
516 54 => 'Tambourine',
517 55 => 'Splash Cymbal',
518 56 => 'Cowbell',
519 57 => 'Crash Cymbal 2',
520 59 => 'Ride Cymbal 2',
521 60 => 'Hi Bongo',
522 61 => 'Low Bongo',
523 62 => 'Mute Hi Conga',
524 63 => 'Open Hi Conga',
525 64 => 'Low Conga',
526 65 => 'High Timbale',
527 66 => 'Low Timbale',
528 67 => 'High Agogo',
529 68 => 'Low Agogo',
530 69 => 'Cabasa',
531 70 => 'Maracas',
532 71 => 'Short Whistle',
533 72 => 'Long Whistle',
534 73 => 'Short Guiro',
535 74 => 'Long Guiro',
536 75 => 'Claves',
537 76 => 'Hi Wood Block',
538 77 => 'Low Wood Block',
539 78 => 'Mute Cuica',
540 79 => 'Open Cuica',
541 80 => 'Mute Triangle',
542 81 => 'Open Triangle'
543 );
545 return @$lookup[$instrument_id];
546 }
549 }
552 ?>