rlm@3: <?php
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | PHP version 5                                                        |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | This source file is subject to version 2 of the GPL license,         |
rlm@3: // | that is bundled with this package in the file license.txt and is     |
rlm@3: // | available through the world-wide-web at the following url:           |
rlm@3: // | http://www.gnu.org/copyleft/gpl.html                                 |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | Authors: James Heinrich <infoØgetid3*org>                            |
rlm@3: // |          Allan Hansen <ahØartemis*dk>                                |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: // | module.audio.midi.php                                                |
rlm@3: // | Module for analyzing midi audio files                                |
rlm@3: // | dependencies: NONE                                                   |
rlm@3: // +----------------------------------------------------------------------+
rlm@3: //
rlm@3: // $Id: module.audio.midi.php,v 1.5 2006/11/02 10:48:01 ah Exp $
rlm@3: 
rlm@3:         
rlm@3:         
rlm@3: class getid3_midi extends getid3_handler
rlm@3: {
rlm@3: 
rlm@3:     public function Analyze() {
rlm@3: 
rlm@3:         $getid3 = $this->getid3;
rlm@3:         
rlm@3:         $getid3->info['midi']['raw'] = array ();
rlm@3:         $info_midi     = &$getid3->info['midi'];
rlm@3:         $info_midi_raw = &$info_midi['raw'];
rlm@3: 
rlm@3:         $getid3->info['fileformat']          = 'midi';
rlm@3:         $getid3->info['audio']['dataformat'] = 'midi';
rlm@3: 
rlm@3:         fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
rlm@3:         $midi_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
rlm@3:                 
rlm@3:         // Magic bytes: 'MThd'
rlm@3:         
rlm@3:         getid3_lib::ReadSequence('BigEndian2Int', $info_midi_raw, $midi_data, 4, 
rlm@3:             array (
rlm@3:                 'headersize'    => 4,
rlm@3:                 'fileformat'    => 2,
rlm@3:                 'tracks'        => 2,
rlm@3:                 'ticksperqnote' => 2
rlm@3:             )
rlm@3:         );
rlm@3:         
rlm@3:         $offset = 14;
rlm@3: 
rlm@3:         for ($i = 0; $i < $info_midi_raw['tracks']; $i++) {
rlm@3:             
rlm@3:             if ((strlen($midi_data) - $offset) < 8) {
rlm@3:                 $midi_data .= fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
rlm@3:             }
rlm@3:             
rlm@3:             $track_id = substr($midi_data, $offset, 4);
rlm@3:             $offset += 4;
rlm@3:             
rlm@3:             if ($track_id != 'MTrk') {
rlm@3:                 throw new getid3_exception('Expecting "MTrk" at '.$offset.', found '.$track_id.' instead');
rlm@3:             }
rlm@3:             
rlm@3:             $track_size = getid3_lib::BigEndian2Int(substr($midi_data, $offset, 4));
rlm@3:             $offset += 4;
rlm@3:                 
rlm@3:             $track_data_array[$i] = substr($midi_data, $offset, $track_size);
rlm@3:             $offset += $track_size;
rlm@3:         }
rlm@3: 
rlm@3:         if (!isset($track_data_array) || !is_array($track_data_array)) {
rlm@3:             throw new getid3_exception('Cannot find MIDI track information');
rlm@3:         }
rlm@3: 
rlm@3:         
rlm@3:         $info_midi['totalticks']          = 0;
rlm@3:         $getid3->info['playtime_seconds'] = 0;
rlm@3:         $current_ms_per_beat              = 500000; // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
rlm@3:         $current_beats_per_min            = 120;    // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
rlm@3:         $ms_per_quarter_note_after        = array ();
rlm@3: 
rlm@3:         foreach ($track_data_array as $track_number => $track_data) {
rlm@3: 
rlm@3:             $events_offset = $last_issued_midi_command = $last_issued_midi_channel = $cumulative_delta_time = $ticks_at_current_bpm = 0;
rlm@3:             
rlm@3:             while ($events_offset < strlen($track_data)) {
rlm@3:                 
rlm@3:                 $event_id = 0;
rlm@3:                 if (isset($midi_events[$track_number]) && is_array($midi_events[$track_number])) {
rlm@3:                     $event_id = count($midi_events[$track_number]);
rlm@3:                 }
rlm@3:                 $delta_time = 0;
rlm@3:                 for ($i = 0; $i < 4; $i++) {
rlm@3:                     $delta_time_byte = ord($track_data{$events_offset++});
rlm@3:                     $delta_time = ($delta_time << 7) + ($delta_time_byte & 0x7F);
rlm@3:                     if ($delta_time_byte & 0x80) {
rlm@3:                         // another byte follows
rlm@3:                     } else {
rlm@3:                         break;
rlm@3:                     }
rlm@3:                 }
rlm@3:                 
rlm@3:                 $cumulative_delta_time += $delta_time;
rlm@3:                 $ticks_at_current_bpm  += $delta_time;
rlm@3:                 
rlm@3:                 $midi_events[$track_number][$event_id]['deltatime'] = $delta_time;
rlm@3:                 
rlm@3:                 $midi_event_channel                                 = ord($track_data{$events_offset++});
rlm@3:                 
rlm@3:                 // OK, normal event - MIDI command has MSB set
rlm@3:                 if ($midi_event_channel & 0x80) {
rlm@3:                     $last_issued_midi_command = $midi_event_channel >> 4;
rlm@3:                     $last_issued_midi_channel = $midi_event_channel & 0x0F;
rlm@3:                 } 
rlm@3:                 
rlm@3:                 // Running event - assume last command
rlm@3:                 else {
rlm@3:                     $events_offset--;
rlm@3:                 }
rlm@3:                 
rlm@3:                 $midi_events[$track_number][$event_id]['eventid'] = $last_issued_midi_command;
rlm@3:                 $midi_events[$track_number][$event_id]['channel'] = $last_issued_midi_channel;
rlm@3:                 
rlm@3:                 switch ($midi_events[$track_number][$event_id]['eventid']) {
rlm@3:                     
rlm@3:                     case 0x8:       // Note off (key is released)
rlm@3:                     case 0x9:       // Note on (key is pressed)
rlm@3:                     case 0xA:       // Key after-touch
rlm@3: 
rlm@3:                         //$notenumber = ord($track_data{$events_offset++});
rlm@3:                         //$velocity   = ord($track_data{$events_offset++});
rlm@3:                         $events_offset += 2;
rlm@3:                         break;
rlm@3:                         
rlm@3:                         
rlm@3:                     case 0xB:       // Control Change
rlm@3:                         
rlm@3:                         //$controllernum = ord($track_data{$events_offset++});
rlm@3:                         //$newvalue      = ord($track_data{$events_offset++});
rlm@3:                         $events_offset += 2;
rlm@3:                         break;
rlm@3:                         
rlm@3:                         
rlm@3:                     case 0xC:       // Program (patch) change
rlm@3:                     
rlm@3:                         $new_program_num = ord($track_data{$events_offset++});
rlm@3: 
rlm@3:                         $info_midi_raw['track'][$track_number]['instrumentid'] = $new_program_num;
rlm@3:                         $info_midi_raw['track'][$track_number]['instrument']   = $track_number == 10 ? getid3_midi::GeneralMIDIpercussionLookup($new_program_num) : getid3_midi::GeneralMIDIinstrumentLookup($new_program_num);
rlm@3:                         break;
rlm@3:                         
rlm@3:                     
rlm@3:                     case 0xD:       // Channel after-touch
rlm@3:                         
rlm@3:                         //$channelnumber = ord($track_data{$events_offset++});
rlm@3:                         break;
rlm@3:                         
rlm@3:                         
rlm@3:                     case 0xE:       // Pitch wheel change (2000H is normal or no change)
rlm@3: 
rlm@3:                         //$changeLSB = ord($track_data{$events_offset++});
rlm@3:                         //$changeMSB = ord($track_data{$events_offset++});
rlm@3:                         //$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
rlm@3:                         $events_offset += 2;
rlm@3:                         break;
rlm@3:                         
rlm@3:     
rlm@3:                     case 0xF:
rlm@3: 
rlm@3:                         if ($midi_events[$track_number][$event_id]['channel'] == 0xF) {
rlm@3:     
rlm@3:                             $meta_event_command = ord($track_data{$events_offset++});
rlm@3:                             $meta_event_length  = ord($track_data{$events_offset++});
rlm@3:                             $meta_event_data    = substr($track_data, $events_offset, $meta_event_length);
rlm@3:                             $events_offset += $meta_event_length;
rlm@3:                             
rlm@3:                             switch ($meta_event_command) {
rlm@3:                             
rlm@3:                                 case 0x00: // Set track sequence number
rlm@3:     
rlm@3:                                     //$track_sequence_number = getid3_lib::BigEndian2Int(substr($meta_event_data, 0, $meta_event_length));
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['seqno'] = $track_sequence_number;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x01: // Text: generic
rlm@3:                                 
rlm@3:                                     $text_generic = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['text'] = $text_generic;
rlm@3:                                     $info_midi['comments']['comment'][] = $text_generic;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x02: // Text: copyright
rlm@3:     
rlm@3:                                     $text_copyright = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['copyright'] = $text_copyright;
rlm@3:                                     $info_midi['comments']['copyright'][] = $text_copyright;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x03: // Text: track name
rlm@3:                                 
rlm@3:                                     $text_trackname = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     $info_midi_raw['track'][$track_number]['name'] = $text_trackname;
rlm@3:                                     break;
rlm@3:                                     
rlm@3:     
rlm@3:                                 case 0x04: // Text: track instrument name
rlm@3:     
rlm@3:                                     //$text_instrument = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['instrument'] = $text_instrument;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x05: // Text: lyrics
rlm@3:     
rlm@3:                                     $text_lyrics = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['lyrics'] = $text_lyrics;
rlm@3:                                     if (!isset($info_midi['lyrics'])) {
rlm@3:                                         $info_midi['lyrics'] = '';
rlm@3:                                     }
rlm@3:                                     $info_midi['lyrics'] .= $text_lyrics . "\n";
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x06: // Text: marker
rlm@3:     
rlm@3:                                     //$text_marker = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['marker'] = $text_marker;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x07: // Text: cue point
rlm@3: 
rlm@3:                                     //$text_cuepoint = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['cuepoint'] = $text_cuepoint;
rlm@3:                                     break;
rlm@3: 
rlm@3:     
rlm@3:                                 case 0x2F: // End Of Track
rlm@3:                                     
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['EOT'] = $cumulative_delta_time;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x51: // Tempo: microseconds / quarter note
rlm@3:     
rlm@3:                                     $current_ms_per_beat = getid3_lib::BigEndian2Int(substr($meta_event_data, 0, $meta_event_length));
rlm@3:                                     $info_midi_raw['events'][$track_number][$cumulative_delta_time]['us_qnote'] = $current_ms_per_beat;
rlm@3:                                     $current_beats_per_min = (1000000 / $current_ms_per_beat) * 60;
rlm@3:                                     $ms_per_quarter_note_after[$cumulative_delta_time] = $current_ms_per_beat;
rlm@3:                                     $ticks_at_current_bpm = 0;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x58: // Time signature
rlm@3:                                     $timesig_numerator   = getid3_lib::BigEndian2Int($meta_event_data[0]);
rlm@3:                                     $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($meta_event_data[1])); // $02 -> x/4, $03 -> x/8, etc
rlm@3:                                     //$timesig_32inqnote   = getid3_lib::BigEndian2Int($meta_event_data[2]);         // number of 32nd notes to the quarter note
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['timesig_32inqnote']   = $timesig_32inqnote;
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['timesig_numerator']   = $timesig_numerator;
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['timesig_denominator'] = $timesig_denominator;
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['timesig_text']        = $timesig_numerator.'/'.$timesig_denominator;
rlm@3:                                     $info_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x59: // Keysignature
rlm@3:                                     
rlm@3:                                     $keysig_sharpsflats = getid3_lib::BigEndian2Int($meta_event_data{0});
rlm@3:                                     if ($keysig_sharpsflats & 0x80) {
rlm@3:                                         // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
rlm@3:                                         $keysig_sharpsflats -= 256;
rlm@3:                                     }
rlm@3:     
rlm@3:                                     $keysig_majorminor  = getid3_lib::BigEndian2Int($meta_event_data{1}); // 0 -> major, 1 -> minor
rlm@3:                                     $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#');
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
rlm@3:                                     //$info_midi_raw['events'][$track_number][$event_id]['keysig_minor']  = (bool)$keysig_majorminor;
rlm@3:                                     //$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');
rlm@3:     
rlm@3:                                     // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
rlm@3:                                     $info_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool)$keysig_majorminor ? 'minor' : 'major');
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 case 0x7F: // Sequencer specific information
rlm@3:     
rlm@3:                                     $custom_data = substr($meta_event_data, 0, $meta_event_length);
rlm@3:                                     break;
rlm@3:     
rlm@3:     
rlm@3:                                 default:
rlm@3:     
rlm@3:                                     $getid3->warning('Unhandled META Event Command: '.$meta_event_command);
rlm@3:                             }
rlm@3:                         }
rlm@3:                         break;
rlm@3:                             
rlm@3:                             
rlm@3:                     default:                                
rlm@3:                         $getid3->warning('Unhandled MIDI Event ID: '.$midi_events[$track_number][$event_id]['eventid']);
rlm@3:                 }
rlm@3:             }
rlm@3:             
rlm@3:             if (($track_number > 0) || (count($track_data_array) == 1)) {
rlm@3:                 $info_midi['totalticks'] = max($info_midi['totalticks'], $cumulative_delta_time);
rlm@3:             }
rlm@3:         }
rlm@3:         
rlm@3:         $previous_tick_offset = null;
rlm@3: 
rlm@3:         ksort($ms_per_quarter_note_after); 
rlm@3:         foreach ($ms_per_quarter_note_after as $tick_offset => $ms_per_beat) {
rlm@3:         
rlm@3:             if (is_null($previous_tick_offset)) {
rlm@3:                 $prev_ms_per_beat     = $ms_per_beat;
rlm@3:                 $previous_tick_offset = $tick_offset;
rlm@3:                 continue;
rlm@3:             }
rlm@3:             
rlm@3:             if ($info_midi['totalticks'] > $tick_offset) {
rlm@3:                 $getid3->info['playtime_seconds'] += (($tick_offset - $previous_tick_offset) / $info_midi_raw['ticksperqnote']) * ($prev_ms_per_beat / 1000000);
rlm@3: 
rlm@3:                 $prev_ms_per_beat     = $ms_per_beat;
rlm@3:                 $previous_tick_offset = $tick_offset;
rlm@3:             }
rlm@3:         }
rlm@3:         
rlm@3:         if ($info_midi['totalticks'] > $previous_tick_offset) {
rlm@3:             $getid3->info['playtime_seconds'] += (($info_midi['totalticks'] - $previous_tick_offset) / $info_midi_raw['ticksperqnote']) * ($ms_per_beat / 1000000);
rlm@3:         }
rlm@3: 
rlm@3:         if (@$getid3->info['playtime_seconds'] > 0) {
rlm@3:             $getid3->info['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
rlm@3:         }
rlm@3: 
rlm@3:         if (!empty($info_midi['lyrics'])) {
rlm@3:             $info_midi['comments']['lyrics'][] = $info_midi['lyrics'];
rlm@3:         }
rlm@3: 
rlm@3:         return true;
rlm@3:     }
rlm@3: 
rlm@3: 
rlm@3:          
rlm@3:     public static function GeneralMIDIinstrumentLookup($instrument_id) {
rlm@3: 
rlm@3:         static $lookup = array (
rlm@3:         
rlm@3:               0 => 'Acoustic Grand',
rlm@3:               1 => 'Bright Acoustic',
rlm@3:               2 => 'Electric Grand',
rlm@3:               3 => 'Honky-Tonk',
rlm@3:               4 => 'Electric Piano 1',
rlm@3:               5 => 'Electric Piano 2',
rlm@3:               6 => 'Harpsichord',
rlm@3:               7 => 'Clavier',
rlm@3:               8 => 'Celesta',
rlm@3:               9 => 'Glockenspiel',
rlm@3:              10 => 'Music Box',
rlm@3:              11 => 'Vibraphone',
rlm@3:              12 => 'Marimba',
rlm@3:              13 => 'Xylophone',
rlm@3:              14 => 'Tubular Bells',
rlm@3:              15 => 'Dulcimer',
rlm@3:              16 => 'Drawbar Organ',
rlm@3:              17 => 'Percussive Organ',
rlm@3:              18 => 'Rock Organ',
rlm@3:              19 => 'Church Organ',
rlm@3:              20 => 'Reed Organ',
rlm@3:              21 => 'Accordian',
rlm@3:              22 => 'Harmonica',
rlm@3:              23 => 'Tango Accordian',
rlm@3:              24 => 'Acoustic Guitar (nylon)',
rlm@3:              25 => 'Acoustic Guitar (steel)',
rlm@3:              26 => 'Electric Guitar (jazz)',
rlm@3:              27 => 'Electric Guitar (clean)',
rlm@3:              28 => 'Electric Guitar (muted)',
rlm@3:              29 => 'Overdriven Guitar',
rlm@3:              30 => 'Distortion Guitar',
rlm@3:              31 => 'Guitar Harmonics',
rlm@3:              32 => 'Acoustic Bass',
rlm@3:              33 => 'Electric Bass (finger)',
rlm@3:              34 => 'Electric Bass (pick)',
rlm@3:              35 => 'Fretless Bass',
rlm@3:              36 => 'Slap Bass 1',
rlm@3:              37 => 'Slap Bass 2',
rlm@3:              38 => 'Synth Bass 1',
rlm@3:              39 => 'Synth Bass 2',
rlm@3:              40 => 'Violin',
rlm@3:              41 => 'Viola',
rlm@3:              42 => 'Cello',
rlm@3:              43 => 'Contrabass',
rlm@3:              44 => 'Tremolo Strings',
rlm@3:              45 => 'Pizzicato Strings',
rlm@3:              46 => 'Orchestral Strings',
rlm@3:              47 => 'Timpani',
rlm@3:              48 => 'String Ensemble 1',
rlm@3:              49 => 'String Ensemble 2',
rlm@3:              50 => 'SynthStrings 1',
rlm@3:              51 => 'SynthStrings 2',
rlm@3:              52 => 'Choir Aahs',
rlm@3:              53 => 'Voice Oohs',
rlm@3:              54 => 'Synth Voice',
rlm@3:              55 => 'Orchestra Hit',
rlm@3:              56 => 'Trumpet',
rlm@3:              57 => 'Trombone',
rlm@3:              58 => 'Tuba',
rlm@3:              59 => 'Muted Trumpet',
rlm@3:              60 => 'French Horn',
rlm@3:              61 => 'Brass Section',
rlm@3:              62 => 'SynthBrass 1',
rlm@3:              63 => 'SynthBrass 2',
rlm@3:              64 => 'Soprano Sax',
rlm@3:              65 => 'Alto Sax',
rlm@3:              66 => 'Tenor Sax',
rlm@3:              67 => 'Baritone Sax',
rlm@3:              68 => 'Oboe',
rlm@3:              69 => 'English Horn',
rlm@3:              70 => 'Bassoon',
rlm@3:              71 => 'Clarinet',
rlm@3:              72 => 'Piccolo',
rlm@3:              73 => 'Flute',
rlm@3:              74 => 'Recorder',
rlm@3:              75 => 'Pan Flute',
rlm@3:              76 => 'Blown Bottle',
rlm@3:              77 => 'Shakuhachi',
rlm@3:              78 => 'Whistle',
rlm@3:              79 => 'Ocarina',
rlm@3:              80 => 'Lead 1 (square)',
rlm@3:              81 => 'Lead 2 (sawtooth)',
rlm@3:              82 => 'Lead 3 (calliope)',
rlm@3:              83 => 'Lead 4 (chiff)',
rlm@3:              84 => 'Lead 5 (charang)',
rlm@3:              85 => 'Lead 6 (voice)',
rlm@3:              86 => 'Lead 7 (fifths)',
rlm@3:              87 => 'Lead 8 (bass + lead)',
rlm@3:              88 => 'Pad 1 (new age)',
rlm@3:              89 => 'Pad 2 (warm)',
rlm@3:              90 => 'Pad 3 (polysynth)',
rlm@3:              91 => 'Pad 4 (choir)',
rlm@3:              92 => 'Pad 5 (bowed)',
rlm@3:              93 => 'Pad 6 (metallic)',
rlm@3:              94 => 'Pad 7 (halo)',
rlm@3:              95 => 'Pad 8 (sweep)',
rlm@3:              96 => 'FX 1 (rain)',
rlm@3:              97 => 'FX 2 (soundtrack)',
rlm@3:              98 => 'FX 3 (crystal)',
rlm@3:              99 => 'FX 4 (atmosphere)',
rlm@3:             100 => 'FX 5 (brightness)',
rlm@3:             101 => 'FX 6 (goblins)',
rlm@3:             102 => 'FX 7 (echoes)',
rlm@3:             103 => 'FX 8 (sci-fi)',
rlm@3:             104 => 'Sitar',
rlm@3:             105 => 'Banjo',
rlm@3:             106 => 'Shamisen',
rlm@3:             107 => 'Koto',
rlm@3:             108 => 'Kalimba',
rlm@3:             109 => 'Bagpipe',
rlm@3:             110 => 'Fiddle',
rlm@3:             111 => 'Shanai',
rlm@3:             112 => 'Tinkle Bell',
rlm@3:             113 => 'Agogo',
rlm@3:             114 => 'Steel Drums',
rlm@3:             115 => 'Woodblock',
rlm@3:             116 => 'Taiko Drum',
rlm@3:             117 => 'Melodic Tom',
rlm@3:             118 => 'Synth Drum',
rlm@3:             119 => 'Reverse Cymbal',
rlm@3:             120 => 'Guitar Fret Noise',
rlm@3:             121 => 'Breath Noise',
rlm@3:             122 => 'Seashore',
rlm@3:             123 => 'Bird Tweet',
rlm@3:             124 => 'Telephone Ring',
rlm@3:             125 => 'Helicopter',
rlm@3:             126 => 'Applause',
rlm@3:             127 => 'Gunshot'
rlm@3:         );
rlm@3: 
rlm@3:         return @$lookup[$instrument_id];
rlm@3:     }   
rlm@3:    
rlm@3:         
rlm@3:         
rlm@3:     public static function GeneralMIDIpercussionLookup($instrument_id) {
rlm@3:         
rlm@3:         static $lookup = array (
rlm@3:         
rlm@3:             35 => 'Acoustic Bass Drum',
rlm@3:             36 => 'Bass Drum 1',
rlm@3:             37 => 'Side Stick',
rlm@3:             38 => 'Acoustic Snare',
rlm@3:             39 => 'Hand Clap',
rlm@3:             40 => 'Electric Snare',
rlm@3:             41 => 'Low Floor Tom',
rlm@3:             42 => 'Closed Hi-Hat',
rlm@3:             43 => 'High Floor Tom',
rlm@3:             44 => 'Pedal Hi-Hat',
rlm@3:             45 => 'Low Tom',
rlm@3:             46 => 'Open Hi-Hat',
rlm@3:             47 => 'Low-Mid Tom',
rlm@3:             48 => 'Hi-Mid Tom',
rlm@3:             49 => 'Crash Cymbal 1',
rlm@3:             50 => 'High Tom',
rlm@3:             51 => 'Ride Cymbal 1',
rlm@3:             52 => 'Chinese Cymbal',
rlm@3:             53 => 'Ride Bell',
rlm@3:             54 => 'Tambourine',
rlm@3:             55 => 'Splash Cymbal',
rlm@3:             56 => 'Cowbell',
rlm@3:             57 => 'Crash Cymbal 2',
rlm@3:             59 => 'Ride Cymbal 2',
rlm@3:             60 => 'Hi Bongo',
rlm@3:             61 => 'Low Bongo',
rlm@3:             62 => 'Mute Hi Conga',
rlm@3:             63 => 'Open Hi Conga',
rlm@3:             64 => 'Low Conga',
rlm@3:             65 => 'High Timbale',
rlm@3:             66 => 'Low Timbale',
rlm@3:             67 => 'High Agogo',
rlm@3:             68 => 'Low Agogo',
rlm@3:             69 => 'Cabasa',
rlm@3:             70 => 'Maracas',
rlm@3:             71 => 'Short Whistle',
rlm@3:             72 => 'Long Whistle',
rlm@3:             73 => 'Short Guiro',
rlm@3:             74 => 'Long Guiro',
rlm@3:             75 => 'Claves',
rlm@3:             76 => 'Hi Wood Block',
rlm@3:             77 => 'Low Wood Block',
rlm@3:             78 => 'Mute Cuica',
rlm@3:             79 => 'Open Cuica',
rlm@3:             80 => 'Mute Triangle',
rlm@3:             81 => 'Open Triangle'
rlm@3:         );
rlm@3: 
rlm@3:         return @$lookup[$instrument_id];
rlm@3:     }   
rlm@3: 
rlm@3:         
rlm@3: }       
rlm@3:         
rlm@3:         
rlm@3: ?>