<?
///////////////////////////////////////////////////////////////////////////////////////////////////
// getid3.php - main parsing file.
// Requires getid3.genres.php
//
// getID3() by James Heinrich <getid3@silisoftware.com>
// available at http://www.silisoftware.com
// and http://www.hotscripts.com/Detailed/10092.html
//
// This code is released under the GNU GPL:
// http://www.gnu.org/copyleft/gpl.html
//
// If you do use this code somewhere, send me an email and tell
// me how/where you used it (if you don't mind).
// If you really like it, see www.silisoftware.com for
// donation information :)
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Reads ID3v1 (up to 1.1) and ID3v2 (up to 4.0) MP3 tags
// and outputs the data to a 2-dimensional array:
// array() {
//   ["exist"]=>bool()                  // does this file actually exist?
//   ["filesize"]=>int()                // in bytes
//   ["getID3version"]=>string()        // ex: "1.10"
//   ["id3"]=>array() {
//     ["id3v1"]=>
//     array(8) {
//       ["title"]=>string()
//       ["artist"]=>string()
//       ["album"]=>string()
//       ["year"]=>string()
//       ["comment"]=>string()
//       ["genreid"]=>int()
//       ["genre"]=>string()
//       ["track"]=>int()
//     }
//     ["id3v2"]=>array(8) {            // ID3v2.x data
//       ["header"]=>bool()
//       ["majorversion"]=>int()
//       ["minorversion"]=>int()
//       ["flags"]["unsynch"]=>string(1)
//       ["flags"]["exthead"]=>string(1)
//       ["flags"]["experim"]=>string(1)
//       ["flags"]["isfooter"]=>string(1)
//       ["headerlength"]=>int()        // in bytes
//       ["title"]=>string()
//       ["artist"]=>string()
//       ["album"]=>string()
//       ["year"]=>string()
//       ["track"]=>int()
//       ["genre"]=>string()
//       ["comment"]=>string()
//     }
//     [<4-char frame name>]=>          // see http://www.id3.org/id3v2.4.0-structure.txt
//     array {                          // for details on which 4-character name represents which data
//       ["flags"]=>string              // NOTE: the actual structure varies depending on the FrameID
//       ["asciidata"]=>string          // approximate translation from text-encodings other than ISO-8859-1 (ie UTF-16, UTF-16BE and UTF-8)
//     }
//   ["version"]=>string()              // MPEG audio version - 1, 2, or 2.5
//   ["layer"]=>string()                // I, II or III
//   ["protection"]=>string()
//   ["bitrate"]=>string()              // in kbps, ex 128
//   ["frequency"]=>string()            // in Hz, ex: 44100
//   ["padding"]=>string()
//   ["private"]=>string()
//   ["channelmode"]=>string()          // mono, stereo, joint stereo or dual channel
//   ["modeextension"]=>string()        // IS, MS, IS+MS for Layer III; 4-31, 8-31, 12-31, 16-31 for Layer I or Layer II
//   ["copyright"]=>string()
//   ["original"]=>string()
//   ["emphasis"]=>string()             // none, 50/15 ms or CCIT J.17
//   ["format"]=>string()               // "mp3" or "zip"
//   ["audiobytes"]=>int()              // bytes of MPEG audio, with ID3v2 headers stripped
//   ["playtime_seconds"]=>float()      // playtime in floating-point seconds
//   ["playtime_string"]=>string()      // playtime in minutes:seconds format
//   ["VBR_bitrate"]=>float()           // exact average bitrate in kbps
//   ["bitratemode"]=>string()          // "VBR" or "CBR"
//   ["VBR_method"]=>string()           // "Xing" or "Fraunhofer"
//   ["VBR_frames"]=>int()              // NOT including the Xing / Fraunhofer (VBRI) header frame
//   ["VBR_bytes"]=>int()               // should be the same as ["audiobytes"]
//   ["VBR_quality"]=>int()             // 0-100 (VBR Fraunhofer only)
// }
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Notes:
//
// If the v2.x parser loses track of where it is, it will return something
// in $id3info["id3v2"]["error"], stating where it hit an error. If nothing is
// returned in that array element, you can assume the entire tag parsed OK.
//
// The <4-char frame name> represents any number of frame names,
// some of which are defined here: http://www.id3.org/id3v2.4.0-frames.txt
// Other non-standard (or deprecated) frame names may be in use as well.
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Usage: $id3info = GetAllMP3info(<filename>);
//        $id3info = GetAllMP3info("/home/mp3s/song.mp3");
//        $id3info = GetAllMP3info("c:\\mp3s\\song.mp3");
//        $id3info = GetAllMP3info("http://www.example.com/song.mp3");
//
// Conforms to ID3v2.4.0 specs as published at
// http://www.id3.org/id3v2.4.0-structure.txt
//
///////////////////////////////////////////////////////////////////////////////////////////////////
define("GETID3VERSION", "1.20");
//
// Version History:
//   1.20:	[January-15-2001]
//          * Bugfix: added fclose() at end of GetAllMP3Info()  (thanks stephane@tekartists.com)
//          * Bugfix: ID3v1 wasn't being parsed if ID3v2 wasn't present  (thanks jakson@gmx.net)
//          * Bugfix: several flags were being parsed incorrectly (the structure had change from
//             ID3v2.3 to ID3v2.4 - v2.3 flags were being incorrectly parsed
//			Support for variable-bitrate (VBR) files, both Xing and Fraunhofer headers
//          All 4-character FrameIDs are now fully parsed according to the specs at http://www.id3.org/id3v2.4.0-frames.txt  
//            This means that most no longer return ["flags"] and ["data"]
//            Note: While I've included support for 30 FrameIDs as defined in the specs, I don't
//            have test files for all of them. If anyone knows of programs that generate any of the
//            untested tags, please email getid3@silisoftware.com ! Here's what's tested and not:
//              Tested: T???, WXXX, USLT, SYLT, COMM, APIC
//            Untested: UFID, TXXX, W???, MCDI, ETCO, MLLT, SYTC, RVA2, EQU2, RVRB, GEOB, PCNT,
//                      POPM, RBUF, AENC, USER, OWNE, COMR, ENCR, GRID, PRIV, SIGN, SEEK, ASPI
//          Much more compact implementation of decodeheader()  (thanks jakson@gmx.net for the idea)
//          ID3v1 genres 126 through 147  (thanks jakson@gmx.net)
//          Added "title", "artist", etc names to ID3v2 data (easier to access than the 4-character
//            FrameIDs of the ID3v2 standard  (thanks jakson@gmx.net)
//          New table_var_dump() function in "check.php"  (based partially on idea by jakson@gmx.net)
//          Seperated ID3v1 retrieval into seperate function
//   1.11:	[December-23-2001]
//          All functions merged into file getid3.php
//          Updated documentation to reflect new returned information
//   1.10:	[December-20-2001]
//          * Bugfix: ID3v1 Track# was incorrectly being parsed whether it existed or not
//			Changed calling procedure to recommend using GetAllMP3info($filename) from getmp3header.php
//          Now includes check.php - example file
//          Checks to see if file is in ZIP or MP3 format (returned in ["format"])
//   1.06:	[November-05-2001]
//			* Bugfix: ID3v2.2 frames weren't being parsed since they use 6-byte rather than 10-byte headers as v2.3+ does
//			  Thanks to Spunk for pointing that out.
//   1.05:	[September-06-2001]
//			* Bugfix: ID3V2 was being parsed even if it didn't exist
//   1.04:	[July-16-2001]
//			* Bugfix: typo in Extended Header section ("strpad" should be "str_pad") (thanks flexter)
//   1.03:	[May-07-2001]
//			* Bugfix: Added missing ["id3v1"]["genreid"] and ["id3v1"]["genre"]
//   1.02:	[May-05-2001]
//			Added "getID3version"
//   1.01:	[May-04-2001]
//			Added support for frame-level de-unsynchronisation (as per ID3v2.4.0 specs)
//			  in addition to ID3v2.3.0 tag-level de-unsynchronisation
//   1.00:	[May-04-2001]
//			Initial public release
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Future Plans:
//   Features:
//     * frame-level decompression
//     * Ogg Vorbis comment support (http://www.xiph.org/ogg/vorbis/doc/v-comment.html)
//   Bugs:
//     * "MP3ext V3.3.17(unicode)" seems to place a non-compliant string at the end of the ID3v2 header
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Reference material:
// * http://www.id3.org/id3v2.4.0-structure.txt
// * http://www.id3.org/id3v2.4.0-frames.txt
// * http://www.id3.org/id3v2.4.0-changes.txt
// * http://www.id3.org/id3v2.3.0.txt
// * http://www.id3.org/mp3frame.html
// * http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
// * http://www.dv.co.yu/mpgscript/mpeghdr.htm
// * http://www.mp3-tech.org/programmer/frame_header.html
// * http://users.belgacom.net/gc247244/extra/tag.html
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// The most recent version of getID3() is available at: http://www.silisoftware.com
//
///////////////////////////////////////////////////////////////////////////////////////////////////

function IsUppercaseAlphaNumeric($char) {
	// determines if a character falls within "A-Z" or "0-9"

	$returnvalue = false;
	if ((($char >= 65) && ($char <= 90)) || (($char >= 48) && ($char <= 57))) {
		$returnvalue = true;
	}
	return $returnvalue;
}

function IsValidID3v2FrameName($framename) { // v2.3 & above
	// The frame ID is made out of the characters capital A-Z and 0-9.
	// Identifiers beginning with "X", "Y" and "Z" are for experimental
	// frames and free for everyone to use, without the need to set the
	// experimental bit in the tag header. Bear in mind that someone else
	// might have used the same identifier as you. All other identifiers are
	// either used or reserved for future use.

	$validname = false;
	$char1 = ord(substr($framename, 0, 1));
	$char2 = ord(substr($framename, 1, 1));
	$char3 = ord(substr($framename, 2, 1));
	$char4 = ord(substr($framename, 3, 1));
	if (IsUppercaseAlphaNumeric($char1) &&
		IsUppercaseAlphaNumeric($char2) &&
		IsUppercaseAlphaNumeric($char3) &&
		IsUppercaseAlphaNumeric($char4)) {
			$validname = true;
	}
	return $validname;
}

function IsValidID3v22FrameName($framename) { // v2.2 only
	// The headers of the frames are similar in their construction.
	// They consist of one three character identifier (capital A-Z and 0-9)
	// and one three byte size field, making a total of six bytes. The
	// header is excluded from the size. Identifiers beginning with "X", "Y"
	// and "Z" are for experimental use and free for everyone to use. Have
	// in mind that someone else might have used the same identifier as you.
	// All other identifiers are either used or reserved for future use.
	// This gives us 46656 combinations of frame identifiers. 

	$validname = false;
	$char1 = ord(substr($framename, 0, 1));
	$char2 = ord(substr($framename, 1, 1));
	$char3 = ord(substr($framename, 2, 1));
	if (IsUppercaseAlphaNumeric($char1) &&
		IsUppercaseAlphaNumeric($char2) &&
		IsUppercaseAlphaNumeric($char3)) {
			$validname = true;
	}
	return $validname;
}

function genrelookup($genreid) {
	$genres["0"]   = "Blues";
	$genres["1"]   = "Classic Rock";
	$genres["2"]   = "Country";
	$genres["3"]   = "Dance";
	$genres["4"]   = "Disco";
	$genres["5"]   = "Funk";
	$genres["6"]   = "Grunge";
	$genres["7"]   = "Hip-Hop";
	$genres["8"]   = "Jazz";
	$genres["9"]   = "Metal";
	$genres["10"]  = "New Age";
	$genres["11"]  = "Oldies";
	$genres["12"]  = "Other";
	$genres["13"]  = "Pop";
	$genres["14"]  = "R&B";
	$genres["15"]  = "Rap";
	$genres["16"]  = "Reggae";
	$genres["17"]  = "Rock";
	$genres["18"]  = "Techno";
	$genres["19"]  = "Industrial";
	$genres["20"]  = "Alternative";
	$genres["21"]  = "Ska";
	$genres["22"]  = "Death Metal";
	$genres["23"]  = "Pranks";
	$genres["24"]  = "Soundtrack";
	$genres["25"]  = "Euro-Techno";
	$genres["26"]  = "Ambient";
	$genres["27"]  = "Trip-Hop";
	$genres["28"]  = "Vocal";
	$genres["29"]  = "Jazz+Funk";
	$genres["30"]  = "Fusion";
	$genres["31"]  = "Trance";
	$genres["32"]  = "Classical";
	$genres["33"]  = "Instrumental";
	$genres["34"]  = "Acid";
	$genres["35"]  = "House";
	$genres["36"]  = "Game";
	$genres["37"]  = "Sound Clip";
	$genres["38"]  = "Gospel";
	$genres["39"]  = "Noise";
	$genres["40"]  = "Alt. Rock";
	$genres["41"]  = "Bass";
	$genres["42"]  = "Soul";
	$genres["43"]  = "Punk";
	$genres["44"]  = "Space";
	$genres["45"]  = "Meditative";
	$genres["46"]  = "Instrumental Pop";
	$genres["47"]  = "Instrumental Rock";
	$genres["48"]  = "Ethnic";
	$genres["49"]  = "Gothic";
	$genres["50"]  = "Darkwave";
	$genres["51"]  = "Techno-Industrial";
	$genres["52"]  = "Electronic";
	$genres["53"]  = "Folk/Pop";
	$genres["54"]  = "Eurodance";
	$genres["55"]  = "Dream";
	$genres["56"]  = "Southern Rock";
	$genres["57"]  = "Comedy";
	$genres["58"]  = "Cult";
	$genres["59"]  = "Gangsta";
	$genres["60"]  = "Top 40";
	$genres["61"]  = "Christian Rap";
	$genres["62"]  = "Pop/Funk";
	$genres["63"]  = "Jungle";
	$genres["64"]  = "Native American";
	$genres["65"]  = "Cabaret";
	$genres["66"]  = "New Wave";
	$genres["67"]  = "Psychadelic";
	$genres["68"]  = "Rave";
	$genres["69"]  = "Showtunes";
	$genres["70"]  = "Trailer";
	$genres["71"]  = "Lo-Fi";
	$genres["72"]  = "Tribal";
	$genres["73"]  = "Acid Punk";
	$genres["74"]  = "Acid Jazz";
	$genres["75"]  = "Polka";
	$genres["76"]  = "Retro";
	$genres["77"]  = "Musical";
	$genres["78"]  = "Rock & Roll";
	$genres["79"]  = "Hard Rock";
	$genres["80"]  = "Folk";
	$genres["81"]  = "Folk/Rock";
	$genres["82"]  = "National Folk";
	$genres["83"]  = "Swing";
	$genres["84"]  = "Fast-Fusion";
	$genres["85"]  = "Bebob";
	$genres["86"]  = "Latin";
	$genres["87"]  = "Revival";
	$genres["88"]  = "Celtic";
	$genres["89"]  = "Bluegrass";
	$genres["90"]  = "Avantgarde";
	$genres["91"]  = "Gothic Rock";
	$genres["92"]  = "Progressive Rock";
	$genres["93"]  = "Psychedelic Rock";
	$genres["94"]  = "Symphonic Rock";
	$genres["95"]  = "Slow Rock";
	$genres["96"]  = "Big Band";
	$genres["97"]  = "Chorus";
	$genres["98"]  = "Easy Listening";
	$genres["99"]  = "Acoustic";
	$genres["100"] = "Humour";
	$genres["101"] = "Speech";
	$genres["102"] = "Chanson";
	$genres["103"] = "Opera";
	$genres["104"] = "Chamber Music";
	$genres["105"] = "Sonata";
	$genres["106"] = "Symphony";
	$genres["107"] = "Booty Bass";
	$genres["108"] = "Primus";
	$genres["109"] = "Porn Groove";
	$genres["110"] = "Satire";
	$genres["111"] = "Slow Jam";
	$genres["112"] = "Club";
	$genres["113"] = "Tango";
	$genres["114"] = "Samba";
	$genres["115"] = "Folklore";
	$genres["116"] = "Ballad";
	$genres["117"] = "Power Ballad";
	$genres["118"] = "Rhythmic Soul";
	$genres["119"] = "Freestyle";
	$genres["120"] = "Duet";
	$genres["121"] = "Punk Rock";
	$genres["122"] = "Drum Solo";
	$genres["123"] = "A Cappella";
	$genres["124"] = "Euro-House";
	$genres["125"] = "Dance Hall";
	$genres["126"] = "Goa";
	$genres["127"] = "Drum & Bass";
	$genres["128"] = "Club-House";
	$genres["129"] = "Hardcore";
	$genres["130"] = "Terror";
	$genres["131"] = "Indie";
	$genres["132"] = "BritPop";
	$genres["133"] = "Negerpunk";
	$genres["134"] = "Polsk Punk";
	$genres["135"] = "Beat";
	$genres["136"] = "Christian Gangsta Rap";
	$genres["137"] = "Heavy Metal";
	$genres["138"] = "Black Metal";
	$genres["139"] = "Crossover";
	$genres["140"] = "Contemporary Christian";
	$genres["141"] = "Christian Rock";
	$genres["142"] = "Merengue";
	$genres["143"] = "Salsa";
	$genres["144"] = "Trash Metal";
	$genres["145"] = "Anime";
	$genres["146"] = "Jpop";
	$genres["147"] = "Synthpop";

	return $genres[$genreid];
}

function LookupCurrency($currencyid) {
	$CurrencyLookup["AED"]["country"] = "United Arab Emirates";
	$CurrencyLookup["AFA"]["country"] = "Afghanistan";
	$CurrencyLookup["ALL"]["country"] = "Albania";
	$CurrencyLookup["AMD"]["country"] = "Armenia";
	$CurrencyLookup["ANG"]["country"] = "Netherlands Antilles";
	$CurrencyLookup["AOA"]["country"] = "Angola";
	$CurrencyLookup["ARS"]["country"] = "Argentina";
	$CurrencyLookup["ATS"]["country"] = "Austria";
	$CurrencyLookup["AUD"]["country"] = "Australia";
	$CurrencyLookup["AWG"]["country"] = "Aruba";
	$CurrencyLookup["AZM"]["country"] = "Azerbaijan";
	$CurrencyLookup["BAM"]["country"] = "Bosnia and Herzegovina";
	$CurrencyLookup["BBD"]["country"] = "Barbados";
	$CurrencyLookup["BDT"]["country"] = "Bangladesh";
	$CurrencyLookup["BEF"]["country"] = "Belgium";
	$CurrencyLookup["BGL"]["country"] = "Bulgaria";
	$CurrencyLookup["BHD"]["country"] = "Bahrain";
	$CurrencyLookup["BIF"]["country"] = "Burundi";
	$CurrencyLookup["BMD"]["country"] = "Bermuda";
	$CurrencyLookup["BND"]["country"] = "Brunei Darussalam";
	$CurrencyLookup["BOB"]["country"] = "Bolivia";
	$CurrencyLookup["BRL"]["country"] = "Brazil";
	$CurrencyLookup["BSD"]["country"] = "Bahamas";
	$CurrencyLookup["BTN"]["country"] = "Bhutan";
	$CurrencyLookup["BWP"]["country"] = "Botswana";
	$CurrencyLookup["BYR"]["country"] = "Belarus";
	$CurrencyLookup["BZD"]["country"] = "Belize";
	$CurrencyLookup["CAD"]["country"] = "Canada";
	$CurrencyLookup["CDF"]["country"] = "Congo/Kinshasa";
	$CurrencyLookup["CHF"]["country"] = "Switzerland";
	$CurrencyLookup["CLP"]["country"] = "Chile";
	$CurrencyLookup["CNY"]["country"] = "China";
	$CurrencyLookup["COP"]["country"] = "Colombia";
	$CurrencyLookup["CRC"]["country"] = "Costa Rica";
	$CurrencyLookup["CUP"]["country"] = "Cuba";
	$CurrencyLookup["CVE"]["country"] = "Cape Verde";
	$CurrencyLookup["CYP"]["country"] = "Cyprus";
	$CurrencyLookup["CZK"]["country"] = "Czech Republic";
	$CurrencyLookup["DEM"]["country"] = "Germany";
	$CurrencyLookup["DJF"]["country"] = "Djibouti";
	$CurrencyLookup["DKK"]["country"] = "Denmark";
	$CurrencyLookup["DOP"]["country"] = "Dominican Republic";
	$CurrencyLookup["DZD"]["country"] = "Algeria";
	$CurrencyLookup["EEK"]["country"] = "Estonia";
	$CurrencyLookup["EGP"]["country"] = "Egypt";
	$CurrencyLookup["ERN"]["country"] = "Eritrea";
	$CurrencyLookup["ESP"]["country"] = "Spain";
	$CurrencyLookup["ETB"]["country"] = "Ethiopia";
	$CurrencyLookup["EUR"]["country"] = "Euro Member Countries";
	$CurrencyLookup["FIM"]["country"] = "Finland";
	$CurrencyLookup["FJD"]["country"] = "Fiji";
	$CurrencyLookup["FKP"]["country"] = "Falkland Islands (Malvinas)";
	$CurrencyLookup["FRF"]["country"] = "France";
	$CurrencyLookup["GBP"]["country"] = "United Kingdom";
	$CurrencyLookup["GEL"]["country"] = "Georgia";
	$CurrencyLookup["GGP"]["country"] = "Guernsey";
	$CurrencyLookup["GHC"]["country"] = "Ghana";
	$CurrencyLookup["GIP"]["country"] = "Gibraltar";
	$CurrencyLookup["GMD"]["country"] = "Gambia";
	$CurrencyLookup["GNF"]["country"] = "Guinea";
	$CurrencyLookup["GRD"]["country"] = "Greece";
	$CurrencyLookup["GTQ"]["country"] = "Guatemala";
	$CurrencyLookup["GYD"]["country"] = "Guyana";
	$CurrencyLookup["HKD"]["country"] = "Hong Kong";
	$CurrencyLookup["HNL"]["country"] = "Honduras";
	$CurrencyLookup["HRK"]["country"] = "Croatia";
	$CurrencyLookup["HTG"]["country"] = "Haiti";
	$CurrencyLookup["HUF"]["country"] = "Hungary";
	$CurrencyLookup["IDR"]["country"] = "Indonesia";
	$CurrencyLookup["IEP"]["country"] = "Ireland (Eire)";
	$CurrencyLookup["ILS"]["country"] = "Israel";
	$CurrencyLookup["IMP"]["country"] = "Isle of Man";
	$CurrencyLookup["INR"]["country"] = "India";
	$CurrencyLookup["IQD"]["country"] = "Iraq";
	$CurrencyLookup["IRR"]["country"] = "Iran";
	$CurrencyLookup["ISK"]["country"] = "Iceland";
	$CurrencyLookup["ITL"]["country"] = "Italy";
	$CurrencyLookup["JEP"]["country"] = "Jersey";
	$CurrencyLookup["JMD"]["country"] = "Jamaica";
	$CurrencyLookup["JOD"]["country"] = "Jordan";
	$CurrencyLookup["JPY"]["country"] = "Japan";
	$CurrencyLookup["KES"]["country"] = "Kenya";
	$CurrencyLookup["KGS"]["country"] = "Kyrgyzstan";
	$CurrencyLookup["KHR"]["country"] = "Cambodia";
	$CurrencyLookup["KMF"]["country"] = "Comoros";
	$CurrencyLookup["KPW"]["country"] = "Korea";
	$CurrencyLookup["KWD"]["country"] = "Kuwait";
	$CurrencyLookup["KYD"]["country"] = "Cayman Islands";
	$CurrencyLookup["KZT"]["country"] = "Kazakstan";
	$CurrencyLookup["LAK"]["country"] = "Laos";
	$CurrencyLookup["LBP"]["country"] = "Lebanon";
	$CurrencyLookup["LKR"]["country"] = "Sri Lanka";
	$CurrencyLookup["LRD"]["country"] = "Liberia";
	$CurrencyLookup["LSL"]["country"] = "Lesotho";
	$CurrencyLookup["LTL"]["country"] = "Lithuania";
	$CurrencyLookup["LUF"]["country"] = "Luxembourg";
	$CurrencyLookup["LVL"]["country"] = "Latvia";
	$CurrencyLookup["LYD"]["country"] = "Libya";
	$CurrencyLookup["MAD"]["country"] = "Morocco";
	$CurrencyLookup["MDL"]["country"] = "Moldova";
	$CurrencyLookup["MGF"]["country"] = "Madagascar";
	$CurrencyLookup["MKD"]["country"] = "Macedonia";
	$CurrencyLookup["MMK"]["country"] = "Myanmar (Burma)";
	$CurrencyLookup["MNT"]["country"] = "Mongolia";
	$CurrencyLookup["MOP"]["country"] = "Macau";
	$CurrencyLookup["MRO"]["country"] = "Mauritania";
	$CurrencyLookup["MTL"]["country"] = "Malta";
	$CurrencyLookup["MUR"]["country"] = "Mauritius";
	$CurrencyLookup["MVR"]["country"] = "Maldives (Maldive Islands)";
	$CurrencyLookup["MWK"]["country"] = "Malawi";
	$CurrencyLookup["MXN"]["country"] = "Mexico";
	$CurrencyLookup["MYR"]["country"] = "Malaysia";
	$CurrencyLookup["MZM"]["country"] = "Mozambique";
	$CurrencyLookup["NAD"]["country"] = "Namibia";
	$CurrencyLookup["NGN"]["country"] = "Nigeria";
	$CurrencyLookup["NIO"]["country"] = "Nicaragua";
	$CurrencyLookup["NLG"]["country"] = "Netherlands (Holland)";
	$CurrencyLookup["NOK"]["country"] = "Norway";
	$CurrencyLookup["NPR"]["country"] = "Nepal";
	$CurrencyLookup["NZD"]["country"] = "New Zealand";
	$CurrencyLookup["OMR"]["country"] = "Oman";
	$CurrencyLookup["PAB"]["country"] = "Panama";
	$CurrencyLookup["PEN"]["country"] = "Peru";
	$CurrencyLookup["PGK"]["country"] = "Papua New Guinea";
	$CurrencyLookup["PHP"]["country"] = "Philippines";
	$CurrencyLookup["PKR"]["country"] = "Pakistan";
	$CurrencyLookup["PLN"]["country"] = "Poland";
	$CurrencyLookup["PTE"]["country"] = "Portugal";
	$CurrencyLookup["PYG"]["country"] = "Paraguay";
	$CurrencyLookup["QAR"]["country"] = "Qatar";
	$CurrencyLookup["ROL"]["country"] = "Romania";
	$CurrencyLookup["RUR"]["country"] = "Russia";
	$CurrencyLookup["RWF"]["country"] = "Rwanda";
	$CurrencyLookup["SAR"]["country"] = "Saudi Arabia";
	$CurrencyLookup["SBD"]["country"] = "Solomon Islands";
	$CurrencyLookup["SCR"]["country"] = "Seychelles";
	$CurrencyLookup["SDD"]["country"] = "Sudan";
	$CurrencyLookup["SEK"]["country"] = "Sweden";
	$CurrencyLookup["SGD"]["country"] = "Singapore";
	$CurrencyLookup["SHP"]["country"] = "Saint Helena";
	$CurrencyLookup["SIT"]["country"] = "Slovenia";
	$CurrencyLookup["SKK"]["country"] = "Slovakia";
	$CurrencyLookup["SLL"]["country"] = "Sierra Leone";
	$CurrencyLookup["SOS"]["country"] = "Somalia";
	$CurrencyLookup["SPL"]["country"] = "Seborga";
	$CurrencyLookup["SRG"]["country"] = "Suriname";
	$CurrencyLookup["STD"]["country"] = "São Tome and Principe";
	$CurrencyLookup["SVC"]["country"] = "El Salvador";
	$CurrencyLookup["SYP"]["country"] = "Syria";
	$CurrencyLookup["SZL"]["country"] = "Swaziland";
	$CurrencyLookup["THB"]["country"] = "Thailand";
	$CurrencyLookup["TJR"]["country"] = "Tajikistan";
	$CurrencyLookup["TMM"]["country"] = "Turkmenistan";
	$CurrencyLookup["TND"]["country"] = "Tunisia";
	$CurrencyLookup["TOP"]["country"] = "Tonga";
	$CurrencyLookup["TRL"]["country"] = "Turkey";
	$CurrencyLookup["TTD"]["country"] = "Trinidad and Tobago";
	$CurrencyLookup["TVD"]["country"] = "Tuvalu";
	$CurrencyLookup["TWD"]["country"] = "Taiwan";
	$CurrencyLookup["TZS"]["country"] = "Tanzania";
	$CurrencyLookup["UAH"]["country"] = "Ukraine";
	$CurrencyLookup["UGX"]["country"] = "Uganda";
	$CurrencyLookup["USD"]["country"] = "United States of America";
	$CurrencyLookup["UYU"]["country"] = "Uruguay";
	$CurrencyLookup["UZS"]["country"] = "Uzbekistan";
	$CurrencyLookup["VAL"]["country"] = "Vatican City";
	$CurrencyLookup["VEB"]["country"] = "Venezuela";
	$CurrencyLookup["VND"]["country"] = "Viet Nam";
	$CurrencyLookup["VUV"]["country"] = "Vanuatu";
	$CurrencyLookup["WST"]["country"] = "Samoa";
	$CurrencyLookup["XAF"]["country"] = "Communauté Financière Africaine";
	$CurrencyLookup["XAG"]["country"] = "Silver";
	$CurrencyLookup["XAU"]["country"] = "Gold";
	$CurrencyLookup["XCD"]["country"] = "East Caribbean";
	$CurrencyLookup["XDR"]["country"] = "International Monetary Fund";
	$CurrencyLookup["XPD"]["country"] = "Palladium";
	$CurrencyLookup["XPF"]["country"] = "Comptoirs Français du Pacifique";
	$CurrencyLookup["XPT"]["country"] = "Platinum";
	$CurrencyLookup["YER"]["country"] = "Yemen";
	$CurrencyLookup["YUM"]["country"] = "Yugoslavia";
	$CurrencyLookup["ZAR"]["country"] = "South Africa";
	$CurrencyLookup["ZMK"]["country"] = "Zambia";
	$CurrencyLookup["ZWD"]["country"] = "Zimbabwe";
	$CurrencyLookup["AED"]["units"] = "Dirhams";
	$CurrencyLookup["AFA"]["units"] = "Afghanis";
	$CurrencyLookup["ALL"]["units"] = "Leke";
	$CurrencyLookup["AMD"]["units"] = "Drams";
	$CurrencyLookup["ANG"]["units"] = "Guilders";
	$CurrencyLookup["AOA"]["units"] = "Kwanza";
	$CurrencyLookup["ARS"]["units"] = "Pesos";
	$CurrencyLookup["ATS"]["units"] = "Schillings";
	$CurrencyLookup["AUD"]["units"] = "Dollars";
	$CurrencyLookup["AWG"]["units"] = "Guilders";
	$CurrencyLookup["AZM"]["units"] = "Manats";
	$CurrencyLookup["BAM"]["units"] = "Convertible Marka";
	$CurrencyLookup["BBD"]["units"] = "Dollars";
	$CurrencyLookup["BDT"]["units"] = "Taka";
	$CurrencyLookup["BEF"]["units"] = "Francs";
	$CurrencyLookup["BGL"]["units"] = "Leva";
	$CurrencyLookup["BHD"]["units"] = "Dinars";
	$CurrencyLookup["BIF"]["units"] = "Francs";
	$CurrencyLookup["BMD"]["units"] = "Dollars";
	$CurrencyLookup["BND"]["units"] = "Dollars";
	$CurrencyLookup["BOB"]["units"] = "Bolivianos";
	$CurrencyLookup["BRL"]["units"] = "Brazil Real";
	$CurrencyLookup["BSD"]["units"] = "Dollars";
	$CurrencyLookup["BTN"]["units"] = "Ngultrum";
	$CurrencyLookup["BWP"]["units"] = "Pulas";
	$CurrencyLookup["BYR"]["units"] = "Rubles";
	$CurrencyLookup["BZD"]["units"] = "Dollars";
	$CurrencyLookup["CAD"]["units"] = "Dollars";
	$CurrencyLookup["CDF"]["units"] = "Congolese Francs";
	$CurrencyLookup["CHF"]["units"] = "Francs";
	$CurrencyLookup["CLP"]["units"] = "Pesos";
	$CurrencyLookup["CNY"]["units"] = "Yuan Renminbi";
	$CurrencyLookup["COP"]["units"] = "Pesos";
	$CurrencyLookup["CRC"]["units"] = "Colones";
	$CurrencyLookup["CUP"]["units"] = "Pesos";
	$CurrencyLookup["CVE"]["units"] = "Escudos";
	$CurrencyLookup["CYP"]["units"] = "Pounds";
	$CurrencyLookup["CZK"]["units"] = "Koruny";
	$CurrencyLookup["DEM"]["units"] = "Deutsche Marks";
	$CurrencyLookup["DJF"]["units"] = "Francs";
	$CurrencyLookup["DKK"]["units"] = "Kroner";
	$CurrencyLookup["DOP"]["units"] = "Pesos";
	$CurrencyLookup["DZD"]["units"] = "Algeria Dinars";
	$CurrencyLookup["EEK"]["units"] = "Krooni";
	$CurrencyLookup["EGP"]["units"] = "Pounds";
	$CurrencyLookup["ERN"]["units"] = "Nakfa";
	$CurrencyLookup["ESP"]["units"] = "Pesetas";
	$CurrencyLookup["ETB"]["units"] = "Birr";
	$CurrencyLookup["EUR"]["units"] = "Euro";
	$CurrencyLookup["FIM"]["units"] = "Markkaa";
	$CurrencyLookup["FJD"]["units"] = "Dollars";
	$CurrencyLookup["FKP"]["units"] = "Pounds";
	$CurrencyLookup["FRF"]["units"] = "Francs";
	$CurrencyLookup["GBP"]["units"] = "Pounds";
	$CurrencyLookup["GEL"]["units"] = "Lari";
	$CurrencyLookup["GGP"]["units"] = "Pounds";
	$CurrencyLookup["GHC"]["units"] = "Cedis";
	$CurrencyLookup["GIP"]["units"] = "Pounds";
	$CurrencyLookup["GMD"]["units"] = "Dalasi";
	$CurrencyLookup["GNF"]["units"] = "Francs";
	$CurrencyLookup["GRD"]["units"] = "Drachmae";
	$CurrencyLookup["GTQ"]["units"] = "Quetzales";
	$CurrencyLookup["GYD"]["units"] = "Dollars";
	$CurrencyLookup["HKD"]["units"] = "Dollars";
	$CurrencyLookup["HNL"]["units"] = "Lempiras";
	$CurrencyLookup["HRK"]["units"] = "Kuna";
	$CurrencyLookup["HTG"]["units"] = "Gourdes";
	$CurrencyLookup["HUF"]["units"] = "Forints";
	$CurrencyLookup["IDR"]["units"] = "Rupiahs";
	$CurrencyLookup["IEP"]["units"] = "Pounds";
	$CurrencyLookup["ILS"]["units"] = "New Shekels";
	$CurrencyLookup["IMP"]["units"] = "Pounds";
	$CurrencyLookup["INR"]["units"] = "Rupees";
	$CurrencyLookup["IQD"]["units"] = "Dinars";
	$CurrencyLookup["IRR"]["units"] = "Rials";
	$CurrencyLookup["ISK"]["units"] = "Kronur";
	$CurrencyLookup["ITL"]["units"] = "Lire";
	$CurrencyLookup["JEP"]["units"] = "Pounds";
	$CurrencyLookup["JMD"]["units"] = "Dollars";
	$CurrencyLookup["JOD"]["units"] = "Dinars";
	$CurrencyLookup["JPY"]["units"] = "Yen";
	$CurrencyLookup["KES"]["units"] = "Shillings";
	$CurrencyLookup["KGS"]["units"] = "Soms";
	$CurrencyLookup["KHR"]["units"] = "Riels";
	$CurrencyLookup["KMF"]["units"] = "Francs";
	$CurrencyLookup["KPW"]["units"] = "Won";
	$CurrencyLookup["KWD"]["units"] = "Dinars";
	$CurrencyLookup["KYD"]["units"] = "Dollars";
	$CurrencyLookup["KZT"]["units"] = "Tenge";
	$CurrencyLookup["LAK"]["units"] = "Kips";
	$CurrencyLookup["LBP"]["units"] = "Pounds";
	$CurrencyLookup["LKR"]["units"] = "Rupees";
	$CurrencyLookup["LRD"]["units"] = "Dollars";
	$CurrencyLookup["LSL"]["units"] = "Maloti";
	$CurrencyLookup["LTL"]["units"] = "Litai";
	$CurrencyLookup["LUF"]["units"] = "Francs";
	$CurrencyLookup["LVL"]["units"] = "Lati";
	$CurrencyLookup["LYD"]["units"] = "Dinars";
	$CurrencyLookup["MAD"]["units"] = "Dirhams";
	$CurrencyLookup["MDL"]["units"] = "Lei";
	$CurrencyLookup["MGF"]["units"] = "Malagasy Francs";
	$CurrencyLookup["MKD"]["units"] = "Denars";
	$CurrencyLookup["MMK"]["units"] = "Kyats";
	$CurrencyLookup["MNT"]["units"] = "Tugriks";
	$CurrencyLookup["MOP"]["units"] = "Patacas";
	$CurrencyLookup["MRO"]["units"] = "Ouguiyas";
	$CurrencyLookup["MTL"]["units"] = "Liri";
	$CurrencyLookup["MUR"]["units"] = "Rupees";
	$CurrencyLookup["MVR"]["units"] = "Rufiyaa";
	$CurrencyLookup["MWK"]["units"] = "Kwachas";
	$CurrencyLookup["MXN"]["units"] = "Pesos";
	$CurrencyLookup["MYR"]["units"] = "Ringgits";
	$CurrencyLookup["MZM"]["units"] = "Meticais";
	$CurrencyLookup["NAD"]["units"] = "Dollars";
	$CurrencyLookup["NGN"]["units"] = "Nairas";
	$CurrencyLookup["NIO"]["units"] = "Gold Cordobas";
	$CurrencyLookup["NLG"]["units"] = "Guilders";
	$CurrencyLookup["NOK"]["units"] = "Krone";
	$CurrencyLookup["NPR"]["units"] = "Nepal Rupees";
	$CurrencyLookup["NZD"]["units"] = "Dollars";
	$CurrencyLookup["OMR"]["units"] = "Rials";
	$CurrencyLookup["PAB"]["units"] = "Balboa";
	$CurrencyLookup["PEN"]["units"] = "Nuevos Soles";
	$CurrencyLookup["PGK"]["units"] = "Kina";
	$CurrencyLookup["PHP"]["units"] = "Pesos";
	$CurrencyLookup["PKR"]["units"] = "Rupees";
	$CurrencyLookup["PLN"]["units"] = "Zlotych";
	$CurrencyLookup["PTE"]["units"] = "Escudos";
	$CurrencyLookup["PYG"]["units"] = "Guarani";
	$CurrencyLookup["QAR"]["units"] = "Rials";
	$CurrencyLookup["ROL"]["units"] = "Lei";
	$CurrencyLookup["RUR"]["units"] = "Rubles";
	$CurrencyLookup["RWF"]["units"] = "Rwanda Francs";
	$CurrencyLookup["SAR"]["units"] = "Riyals";
	$CurrencyLookup["SBD"]["units"] = "Dollars";
	$CurrencyLookup["SCR"]["units"] = "Rupees";
	$CurrencyLookup["SDD"]["units"] = "Dinars";
	$CurrencyLookup["SEK"]["units"] = "Kronor";
	$CurrencyLookup["SGD"]["units"] = "Dollars";
	$CurrencyLookup["SHP"]["units"] = "Pounds";
	$CurrencyLookup["SIT"]["units"] = "Tolars";
	$CurrencyLookup["SKK"]["units"] = "Koruny";
	$CurrencyLookup["SLL"]["units"] = "Leones";
	$CurrencyLookup["SOS"]["units"] = "Shillings";
	$CurrencyLookup["SPL"]["units"] = "Luigini";
	$CurrencyLookup["SRG"]["units"] = "Guilders";
	$CurrencyLookup["STD"]["units"] = "Dobras";
	$CurrencyLookup["SVC"]["units"] = "Colones";
	$CurrencyLookup["SYP"]["units"] = "Pounds";
	$CurrencyLookup["SZL"]["units"] = "Emalangeni";
	$CurrencyLookup["THB"]["units"] = "Baht";
	$CurrencyLookup["TJR"]["units"] = "Rubles";
	$CurrencyLookup["TMM"]["units"] = "Manats";
	$CurrencyLookup["TND"]["units"] = "Dinars";
	$CurrencyLookup["TOP"]["units"] = "Pa'anga";
	$CurrencyLookup["TRL"]["units"] = "Liras";
	$CurrencyLookup["TTD"]["units"] = "Dollars";
	$CurrencyLookup["TVD"]["units"] = "Tuvalu Dollars";
	$CurrencyLookup["TWD"]["units"] = "New Dollars";
	$CurrencyLookup["TZS"]["units"] = "Shillings";
	$CurrencyLookup["UAH"]["units"] = "Hryvnia";
	$CurrencyLookup["UGX"]["units"] = "Shillings";
	$CurrencyLookup["USD"]["units"] = "Dollars";
	$CurrencyLookup["UYU"]["units"] = "Pesos";
	$CurrencyLookup["UZS"]["units"] = "Sums";
	$CurrencyLookup["VAL"]["units"] = "Lire";
	$CurrencyLookup["VEB"]["units"] = "Bolivares";
	$CurrencyLookup["VND"]["units"] = "Dong";
	$CurrencyLookup["VUV"]["units"] = "Vatu";
	$CurrencyLookup["WST"]["units"] = "Tala";
	$CurrencyLookup["XAF"]["units"] = "Francs";
	$CurrencyLookup["XAG"]["units"] = "Ounces";
	$CurrencyLookup["XAU"]["units"] = "Ounces";
	$CurrencyLookup["XCD"]["units"] = "Dollars";
	$CurrencyLookup["XDR"]["units"] = "Special Drawing Rights";
	$CurrencyLookup["XPD"]["units"] = "Ounces";
	$CurrencyLookup["XPF"]["units"] = "Francs";
	$CurrencyLookup["XPT"]["units"] = "Ounces";
	$CurrencyLookup["YER"]["units"] = "Rials";
	$CurrencyLookup["YUM"]["units"] = "New Dinars";
	$CurrencyLookup["ZAR"]["units"] = "Rand";
	$CurrencyLookup["ZMK"]["units"] = "Kwacha";
	$CurrencyLookup["ZWD"]["units"] = "Zimbabwe Dollars";

	return $CurrencyLookup[$currencyid];
}

function RoughTranslateUnicodeToASCII($rawdata, $frame_textencoding) {
	$asciidata = "";
	switch ($frame_textencoding) { // rough translation of data for application that can't handle Unicode data
		case 0:
			$asciidata .= $rawdata;
			break;
		case 1:
			$asciidata .= str_replace(chr(0), "", substr($rawdata, 2));
			break;
		case 2:
			$asciidata .= str_replace(chr(0), "", $rawdata);
			break;
		case 3:
			$frame_asciidescription = str_replace(chr(0), "", $rawdata);
			for ($i=0;$i<strlen($frame_asciidescription);$i++) {
				if (ord(substr($frame_asciidescription, $i, 1)) <= 127) {
					$asciidata .= substr($frame_asciidescription, $i, 1);
				} else {
					$asciidata .= "_";
					$i += substr_count(decbin(ord(substr($frame_asciidescription, $i, 1))), "1") - 1;
				}
			}
			break;
	}
	return $asciidata;
}

function bighexdec($hexnumber) {
	$numlength = strlen($hexnumber);
	$decnumber = 0;
	for ($x=1;$x<=$numlength;$x++) {
		$place = $numlength - $x;
		$operand = hexdec(substr($hexnumber, $place, 1));
		$exponent = pow(16, $x-1);
		$decValue = $operand * $exponent;
		$decnumber += $decValue;
	}
	return $decnumber;
}

function getID3($filename) {
	$fd = fopen($filename, "rb");
	getID3Filepointer($fd);
}

function getID3v1Filepointer($fd) {
	fseek($fd, -128, SEEK_END);
	$id3_tag = fread ($fd, 3);
	if ($id3_tag == "TAG") {
		$id3v1info["title"]   = fread($fd, 30);
		$id3v1info["artist"]  = fread($fd, 30);
		$id3v1info["album"]   = fread($fd, 30);
		$id3v1info["year"]    = fread($fd, 4);
		$id3v1info["comment"] = fread($fd, 30);
		$id3v1info["genreid"] = ord(fread($fd, 1));
		if (ord(substr($id3v1info["comment"], 28, 1)) === 0) {
			$id3v1info["track"] = ord(substr($id3v1info["comment"], 29, 1));
			$id3v1info["comment"] = substr($id3v1info["comment"], 0, 28);
		}
		$id3v1info["genre"] = genrelookup($id3v1info["genreid"]);
	}
	return $id3v1info;
}

function getID3v2Filepointer($fd) {
	// This is the main function that does all the work, parsing ID3v2

//		Overall tag structure:
//		+-----------------------------+
//		|      Header (10 bytes)      |
//		+-----------------------------+
//		|       Extended Header       |
//		| (variable length, OPTIONAL) |
//		+-----------------------------+
//		|   Frames (variable length)  |
//		+-----------------------------+
//		|           Padding           |
//		| (variable length, OPTIONAL) |
//		+-----------------------------+
//		| Footer (10 bytes, OPTIONAL) |
//		+-----------------------------+

//	Header
	rewind($fd);
	$header = fread ($fd, 10);
//		The first three bytes of the tag are always "ID3" to indicate that this is an ID3v2 tag,
//		directly followed by the two version bytes. The first byte of ID3v2 version is it's major
//		version, while the second byte is its revision number.
	$id3_identifier = substr($header, 0, 3);
	if ($id3_identifier == "ID3") {
		$id3info["header"] = true;
		$id3info["majorversion"] = ord(substr($header, 3, 1));
		$id3info["minorversion"] = ord(substr($header, 4, 1));
	} // end if ($id3_identifier == "ID3")

	if ($id3info["header"] && ($id3info["majorversion"] <= 4)) { // this script probably won't correctly parse ID3v5.x and above.

//		ID3v2 flags %abc00000
//		The version is followed by one the ID3v2 flags field, of which currently only three flags are used.
//		a - Unsynchronisation:
//			Bit 7 in the 'ID3v2 flags' indicates whether or not unsynchronisation is used; a set bit indicates usage.
//		b - Extended header:
//			The second bit (bit 6) indicates whether or not the header is followed by an extended header.
//		c - Experimental indicator:
//			The third bit (bit 5) should be used as an 'experimental indicator'. This flag should always be set when the tag is in an experimental stage.
//		d - Footer present:
//			Bit 4 indicates that a footer (section 3.4) is present at the very end of the tag. A set bit indicates the presence of a footer.
//		All the other flags should be cleared.
		$id3_flags = str_pad(decbin(ord(substr($header, 5, 1))), 8, "0", STR_PAD_LEFT);
		if ($id3info["majorversion"] == 2) {
			$id3info["flags"]["unsynch"]     = substr($id3_flags, 0, 1);
			$id3info["flags"]["compression"] = substr($id3_flags, 1, 1);
		} else if ($id3info["majorversion"] == 3) {
			$id3info["flags"]["unsynch"]  = substr($id3_flags, 0, 1);
			$id3info["flags"]["exthead"]  = substr($id3_flags, 1, 1);
			$id3info["flags"]["experim"]  = substr($id3_flags, 2, 1);
		} else if ($id3info["majorversion"] == 4) {
			$id3info["flags"]["unsynch"]  = substr($id3_flags, 0, 1);
			$id3info["flags"]["exthead"]  = substr($id3_flags, 1, 1);
			$id3info["flags"]["experim"]  = substr($id3_flags, 2, 1);
			$id3info["flags"]["isfooter"] = substr($id3_flags, 3, 1);
		}

//		The ID3v2 tag size is encoded with four bytes where the most significant bit (bit 7) is set to
//		zero in every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long
//		tag is represented as $00 00 02 01. The ID3v2 tag size is the size of the complete tag after
//		unsychronisation, including padding, excluding the header but not excluding the extended header
//		(total tag size - 10). Only 28 bits (representing up to 256MB) are used in the size description
//		to avoid the introducuction of 'false syncsignals'.
		$id3_headerlength1 = substr(str_pad(decbin(ord(substr($header, 6, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
		$id3_headerlength2 = substr(str_pad(decbin(ord(substr($header, 7, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
		$id3_headerlength3 = substr(str_pad(decbin(ord(substr($header, 8, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
		$id3_headerlength4 = substr(str_pad(decbin(ord(substr($header, 9, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
		$id3info["headerlength"] = bindec($id3_headerlength1.$id3_headerlength2.$id3_headerlength3.$id3_headerlength4);

//	Extended Header
		if ($id3info["flags"]["exthead"]) {
//			Extended header size   4 * %0xxxxxxx
//			Number of flag bytes       $01
//			Extended Flags             $xx
//			Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer.
			$extheader = fread ($fd, 4);
			$id3_extheaderlength1 = substr(str_pad(decbin(ord(substr($extheader, 6, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
			$id3_extheaderlength2 = substr(str_pad(decbin(ord(substr($extheader, 7, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
			$id3_extheaderlength3 = substr(str_pad(decbin(ord(substr($extheader, 8, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
			$id3_extheaderlength4 = substr(str_pad(decbin(ord(substr($extheader, 9, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
			$id3info["extheaderlength"] = bindec($id3_extheaderlength1.$id3_extheaderlength2.$id3_extheaderlength3.$id3_extheaderlength4);

//			The extended flags field, with its size described by 'number of flag  bytes', is defined as:
//				%0bcd0000
//			b - Tag is an update
//				Flag data length       $00
//			c - CRC data present
//				Flag data length       $05
//				Total frame CRC    5 * %0xxxxxxx
//			d - Tag restrictions
//				Flag data length       $01
//				Restrictions           %ppqrrstt
//				p - Tag size restrictions
//				q - Text encoding restrictions
//				r - Text fields size restrictions
//				s - Image encoding restrictions
//				t - Image size restrictions
			$extheaderflagbytes = fread ($fd, 1);
			$extheaderflags     = fread ($fd, $extheaderflagbytes);
			$id3_exthead_flags = str_pad(decbin(ord(substr($header, 5, 1))), 8, "0", STR_PAD_LEFT);
			$id3info["exthead_flags"]["update"]       = substr($id3_exthead_flags, 1, 1);
			$id3info["exthead_flags"]["CRC"]          = substr($id3_exthead_flags, 2, 1);
			if ($id3info["exthead_flags"]["CRC"]) {
				$extheaderrawCRC = fread ($fd, 5);
				$extheaderrawCRC1 = substr(str_pad(decbin(ord(substr($extheaderrawCRC, 0, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$extheaderrawCRC2 = substr(str_pad(decbin(ord(substr($extheaderrawCRC, 1, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$extheaderrawCRC3 = substr(str_pad(decbin(ord(substr($extheaderrawCRC, 2, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$extheaderrawCRC4 = substr(str_pad(decbin(ord(substr($extheaderrawCRC, 3, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$extheaderrawCRC5 = substr(str_pad(decbin(ord(substr($extheaderrawCRC, 4, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$id3info["exthead_flags"]["CRC"] = str_pad(dechex(bindec(substr($extheaderrawCRC1.$extheaderrawCRC2.$extheaderrawCRC3.$extheaderrawCRC4.$extheaderrawCRC5, 3, 32))), 8, "0", STR_PAD_LEFT);
			}
			$id3info["exthead_flags"]["restrictions"] = substr($id3_exthead_flags, 3, 1);
			if ($id3info["exthead_flags"]["restrictions"]) {
				$extheaderrawrestrictions = fread ($fd, 1);
				$id3info["exthead_flags"]["restrictions_tagsize"]  = bindec(substr(str_pad(decbin(ord($extheaderrawrestrictions)), 8, "0", STR_PAD_LEFT), 0, 2)); //Tag size restrictions
				$id3info["exthead_flags"]["restrictions_textenc"]  = bindec(substr(str_pad(decbin(ord($extheaderrawrestrictions)), 8, "0", STR_PAD_LEFT), 2, 1)); //Text encoding restrictions
				$id3info["exthead_flags"]["restrictions_textsize"] = bindec(substr(str_pad(decbin(ord($extheaderrawrestrictions)), 8, "0", STR_PAD_LEFT), 4, 2)); //Text fields size restrictions
				$id3info["exthead_flags"]["restrictions_imgenc"]   = bindec(substr(str_pad(decbin(ord($extheaderrawrestrictions)), 8, "0", STR_PAD_LEFT), 5, 1)); //Image encoding restrictions
				$id3info["exthead_flags"]["restrictions_imgsize"]  = bindec(substr(str_pad(decbin(ord($extheaderrawrestrictions)), 8, "0", STR_PAD_LEFT), 6, 2)); //Image size restrictions
			}
		} // end extended header

//	Frames

//		All ID3v2 frames consists of one frame header followed by one or more
//		fields containing the actual information. The header is always 10
//		bytes and laid out as follows:
//
//		Frame ID      $xx xx xx xx  (four characters)
//		Size      4 * %0xxxxxxx
//		Flags         $xx xx

		$sizeofframes = $id3info["headerlength"] - $id3info["extheaderlength"];
		if ($id3info["flags"]["isfooter"]) {
			$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
		}
		$framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable

		//	if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
		if ($id3info["flags"]["unsynch"] && ($id3info["majorversion"] <= 3)) {
			$framedata = str_replace(chr(hexdec("FF")).chr(hexdec("00")), chr(hexdec("FF")), $framedata);
		}
//		[in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
//		of on tag level, making it easier to skip frames, increasing the streamability
//		of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
//		there exists an unsynchronised frame, while the new unsynchronisation flag in
//		the frame header [S:4.1.2] indicates unsynchronisation.

		while (strlen($framedata) > 0) { // cycle through until no more frame data is left to parse
			if ($id3info["majorversion"] > 2) {
				// Frame ID  $xx xx xx xx (four characters) 
				// Size      $xx xx xx xx (32-bit integer in v2.3, 32-bit synchsafe in v2.4+)
				// Flags     $xx xx

				$frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
				$framedata = substr($framedata, 10);       // and leave the rest in $framedata

				$frame_name = substr($frame_header, 0, 4);
				if ((ord(substr($frame_name, 0, 1)) == 0) &&
					(ord(substr($frame_name, 1, 1)) == 0) &&
					(ord(substr($frame_name, 2, 1)) == 0) &&
					(ord(substr($frame_name, 3, 1)) == 0)) {
						break; // padding encountered, skip rest of ID3v2 header
				}

				$frame_rawsize = substr($frame_header, 4, 4);

				// ID3v2.3.0 defines the size as a 32-bit integer
				// ID3v2.4.0 defines the size as a 32-bit synchsafe integer (28-bit value)
				if ($id3info["majorversion"] >= 4) {
					$framesize1 = substr(str_pad(decbin(ord(substr($frame_rawsize, 0, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
					$framesize2 = substr(str_pad(decbin(ord(substr($frame_rawsize, 1, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
					$framesize3 = substr(str_pad(decbin(ord(substr($frame_rawsize, 2, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
					$framesize4 = substr(str_pad(decbin(ord(substr($frame_rawsize, 3, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				} else { // <= ID3v2.3.0
					$framesize1 = str_pad(decbin(ord(substr($frame_rawsize, 0, 1))), 8, "0", STR_PAD_LEFT);
					$framesize2 = str_pad(decbin(ord(substr($frame_rawsize, 1, 1))), 8, "0", STR_PAD_LEFT);
					$framesize3 = str_pad(decbin(ord(substr($frame_rawsize, 2, 1))), 8, "0", STR_PAD_LEFT);
					$framesize4 = str_pad(decbin(ord(substr($frame_rawsize, 3, 1))), 8, "0", STR_PAD_LEFT);
				}
				$frame_size = bindec($framesize1.$framesize2.$framesize3.$framesize4);

				if (($frame_size <= strlen($framedata)) &&	(IsValidID3v2FrameName($frame_name))) {

					$frame_flags = str_pad(decbin(ByteWord2Int(substr($frame_header, 8, 2))), 16, "0", STR_PAD_LEFT);
					if ($id3info["majorversion"] == 3) {
						//	Frame Header Flags
						//	%abc00000 %ijk00000
						//	a - Tag alter preservation
						//	b - File alter preservation
						//	c - Read only
						//	i - Compression
						//	j - Encryption
						//	k - Grouping identity
						$id3info[$frame_name]["flags"]["TagAlterPreservation"]  = substr($frame_flags,  0, 1); // a
						$id3info[$frame_name]["flags"]["FileAlterPreservation"] = substr($frame_flags,  1, 1); // b
						$id3info[$frame_name]["flags"]["ReadOnly"]              = substr($frame_flags,  2, 1); // c
						$id3info[$frame_name]["flags"]["Compression"]           = substr($frame_flags,  8, 1); // i
						$id3info[$frame_name]["flags"]["Encryption"]            = substr($frame_flags,  9, 1); // j
						$id3info[$frame_name]["flags"]["GroupingIdentity"]      = substr($frame_flags, 10, 1); // k
					} else if ($id3info["majorversion"] == 4) {
						//	Frame Header Flags
						//	%0abc0000 %0h00kmnp
						//	a - Tag alter preservation
						//	b - File alter preservation
						//	c - Read only
						//	h - Grouping identity
						//	k - Compression
						//	m - Encryption
						//	n - Unsynchronisation
						//	p - Data length indicator
						$id3info[$frame_name]["flags"]["TagAlterPreservation"]  = substr($frame_flags,  1, 1); // a
						$id3info[$frame_name]["flags"]["FileAlterPreservation"] = substr($frame_flags,  2, 1); // b
						$id3info[$frame_name]["flags"]["ReadOnly"]              = substr($frame_flags,  3, 1); // c
						$id3info[$frame_name]["flags"]["GroupingIdentity"]      = substr($frame_flags,  9, 1); // h
						$id3info[$frame_name]["flags"]["Compression"]           = substr($frame_flags, 12, 1); // k
						$id3info[$frame_name]["flags"]["Encryption"]            = substr($frame_flags, 13, 1); // m
						$id3info[$frame_name]["flags"]["Unsynchronisation"]     = substr($frame_flags, 14, 1); // n
						$id3info[$frame_name]["flags"]["DataLengthIndicator"]   = substr($frame_flags, 15, 1); // p
					}

					$id3info[$frame_name]["data"]  = substr($framedata, 0, $frame_size);
					$framedata = substr($framedata, $frame_size); // leave rest of data for next FrameID

					//	Frame-level de-unsynchronization
					if ($id3info[$frame_name]["flags"]["Unsynchronisation"] == "1") {
						$id3info[$frame_name]["data"] = str_replace(chr(hexdec("FF")).chr(hexdec("00")), chr(hexdec("FF")), $id3info[$frame_name]["data"]);
					}

					//	Frame-level de-compression
					if ($id3info[$frame_name]["flags"]["Compression"] == "1") {
						// it's on the wishlist :)
					}



					// http://www.id3.org/id3v2.4.0-structure.txt
					// Frames that allow different types of text encoding contains
					// a text encoding description byte. Possible encodings:
					// $00  ISO-8859-1.
					//      Terminated with $00.
					// $01  UTF-16 encoded Unicode with BOM. All
					//      strings in the same frame SHALL have the same byteorder.
					//      Terminated with $00 00.
					// $02  UTF-16BE encoded Unicode without BOM.
					//      Terminated with $00 00.
					// $03  UTF-8 encoded Unicode.
					//      Terminated with $00.
					$TextEncodingLookup["encoding"]   = array("ISO-8859-1", "UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder", "UTF-16BE encoded Unicode without BOM", "UTF-8 encoded Unicode");
					$TextEncodingLookup["terminator"] = array(chr(0), chr(0).chr(0), chr(0).chr(0), chr(0));

					if ($frame_name == "UFID") { // 4.1   UFID Unique file identifier
						//   There may be more than one "UFID" frame in a tag,
						//   but only one with the same 'Owner identifier'.
						// <Header for 'Unique file identifier', ID: "UFID">
						// Owner identifier        <text string> $00
						// Identifier              <up to 64 bytes binary data>

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0));
						$frame_idstring = substr($id3info[$frame_name]["data"], 0, $frame_terminatorpos);
						if (ord($frame_idstring) === 0) {
							$frame_idstring = 0;
						}
						$id3info[$frame_name][$frame_idstring]["flags"]     = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_idstring]["data"]      = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen(chr(0)));
						if ($id3info[$frame_name][$frame_idstring]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_idstring]["asciidata"] = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_idstring]["data"], 0);
						}
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "TXXX") { // 4.2.2 TXXX User defined text information frame
						//   There may be more than one "TXXX" frame in each tag,
						//   but only one with the same description.
						// <Header for 'User defined text information frame', ID: "TXXX">
						// Text encoding     $xx
						// Description       <text string according to encoding> $00 (00)
						// Value             <text string according to encoding>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = "";
						}
						$id3info[$frame_name][$frame_arrayindex]["encodingid"]  = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]    = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["flags"]       = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["description"] = $frame_description;
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidescription"] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding);
						}
						$id3info[$frame_name][$frame_arrayindex]["data"]        = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidata"]   = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_arrayindex]["data"], $frame_textencoding);
						}
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if (substr($frame_name, 0, 1) == "T") { // 4.2. T??? Text information frame
						//   There may only be one text information frame of its kind in an tag.
						// <Header for 'Text information frame', ID: "T000" - "TZZZ",
						// excluding "TXXX" described in 4.2.6.>
						// Text encoding                $xx
						// Information                  <text string(s) according to encoding>
						
						$frame_offset = 0;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["data"]        = substr($id3info[$frame_name]["data"], $frame_offset);
						if ($id3info[$frame_name]["flags"]["Compression"] != "1") {
							$id3info[$frame_name]["asciidata"] = RoughTranslateUnicodeToASCII($id3info[$frame_name]["data"], $frame_textencoding);
						}
						$id3info[$frame_name]["encodingid"]  = $frame_textencoding;
						$id3info[$frame_name]["encoding"]    = $TextEncodingLookup["encoding"][$frame_textencoding];


					} else if ($frame_name == "WXXX") { // 4.3.2 WXXX User defined URL link frame
						//   There may be more than one "WXXX" frame in each tag,
						//   but only one with the same description
						// <Header for 'User defined URL link frame', ID: "WXXX">
						// Text encoding     $xx
						// Description       <text string according to encoding> $00 (00)
						// URL               <text string>
						
						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = "";
						}
						$id3info[$frame_name][$frame_arrayindex]["encodingid"]  = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]    = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["URL"]         = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));
						$id3info[$frame_name][$frame_arrayindex]["flags"]       = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["data"]        = $frame_description;
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidata"]   = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_arrayindex]["data"], $frame_textencoding);
						}
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if (substr($frame_name, 0, 1) == "W") { // 4.3. W??? URL link frames
						//   There may only be one URL [URL] link frame of its kind in an
						//   tag, except when stated otherwise in the frame description
						// <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX"
						// described in 4.3.2.>
						// URL              <text string>
						
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$id3info[$frame_name][$frame_arrayindex]["flags"] = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["URL"] = $id3info[$frame_name]["data"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "MCDI") { // 4.4   MCDI Music CD identifier
						//   There may only be one "MCDI" frame in each tag
						// <Header for 'Music CD identifier', ID: "MCDI">
						// CD TOC                <binary data>
						
						// no special processing needed
						
						
					} else if ($frame_name == "ETCO") { // 4.5   ETCO Event timing codes
						//   There may only be one "ETCO" frame in each tag
						// <Header for 'Event timing codes', ID: "ETCO">
						// Time stamp format    $xx
						//   Where time stamp format is:
						// $01  (32-bit value) MPEG frames from beginning of file
						// $02  (32-bit value) milliseconds from beginning of file
						//   Followed by a list of key events in the following format:
						// Type of event   $xx
						// Time stamp      $xx (xx ...)
						//   The 'Time stamp' is set to zero if directly at the beginning of the sound
						//   or after the previous event. All events MUST be sorted in chronological order.

						$EventLookup[hexdec("00")] = "padding (has no meaning)";
						$EventLookup[hexdec("01")] = "end of initial silence";
						$EventLookup[hexdec("02")] = "intro start";
						$EventLookup[hexdec("03")] = "main part start";
						$EventLookup[hexdec("04")] = "outro start";
						$EventLookup[hexdec("05")] = "outro end";
						$EventLookup[hexdec("06")] = "verse start";
						$EventLookup[hexdec("07")] = "refrain start";
						$EventLookup[hexdec("08")] = "interlude start";
						$EventLookup[hexdec("09")] = "theme start";
						$EventLookup[hexdec("0A")] = "variation start";
						$EventLookup[hexdec("0B")] = "key change";
						$EventLookup[hexdec("0C")] = "time change";
						$EventLookup[hexdec("0D")] = "momentary unwanted noise (Snap, Crackle & Pop)";
						$EventLookup[hexdec("0E")] = "sustained noise";
						$EventLookup[hexdec("0F")] = "sustained noise end";
						$EventLookup[hexdec("10")] = "intro end";
						$EventLookup[hexdec("11")] = "main part end";
						$EventLookup[hexdec("12")] = "verse end";
						$EventLookup[hexdec("13")] = "refrain end";
						$EventLookup[hexdec("14")] = "theme end";
						$EventLookup[hexdec("15")] = "profanity";
						$EventLookup[hexdec("16")] = "profanity end";
						for ($i=hexdec("17");$i<=hexdec("DF");$i++) {
							$EventLookup[$i] = "reserved for future use";
						}
						for ($i=hexdec("E0");$i<=hexdec("EF");$i++) {
							$EventLookup[$i] = "not predefined synch 0-F";
						}
						for ($i=hexdec("F0");$i<=hexdec("FC");$i++) {
							$EventLookup[$i] = "reserved for future use";
						}
						$EventLookup[hexdec("FD")] = "audio end (start of silence)";
						$EventLookup[hexdec("FE")] = "audio file ends";
						$EventLookup[hexdec("FF")] = "one more byte of events follows";
						
						$frame_offset = 0;
						$id3info[$frame_name]["timestampformat"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));

						while ($frame_offset < strlen($id3info[$frame_name]["data"])) {
							$frame_arrayindex = count($id3info[$frame_name]) - 3;
							$id3info[$frame_name][$frame_arrayindex]["typeid"] = substr($id3info[$frame_name]["data"], $frame_offset++, 1);
							$id3info[$frame_name][$frame_arrayindex]["type"] = $EventLookup[$id3info[$frame_name][$frame_arrayindex]["typeid"]];
							$id3info[$frame_name][$frame_arrayindex]["timestamp"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));
							$frame_offset += 4;
						}
						
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "MLLT") { // 4.6   MLLT MPEG location lookup table
						//   There may only be one "MLLT" frame in each tag
						// <Header for 'Location lookup table', ID: "MLLT">
						// MPEG frames between reference  $xx xx
						// Bytes between reference        $xx xx xx
						// Milliseconds between reference $xx xx xx
						// Bits for bytes deviation       $xx
						// Bits for milliseconds dev.     $xx
						//   Then for every reference the following data is included;
						// Deviation in bytes         %xxx....
						// Deviation in milliseconds  %xxx....
						
						$frame_offset = 0;
						$id3info[$frame_name]["framesbetweenreferences"] = ByteWord2Int(substr($id3info[$frame_name]["data"], 0, 2));
						$id3info[$frame_name]["bytesbetweenreferences"]  = ByteWord2Int(substr($id3info[$frame_name]["data"], 2, 3));
						$id3info[$frame_name]["msbetweenreferences"]     = ByteWord2Int(substr($id3info[$frame_name]["data"], 5, 3));
						$id3info[$frame_name]["bitsforbytesdeviation"]   = ByteWord2Int(substr($id3info[$frame_name]["data"], 8, 1));
						$id3info[$frame_name]["bitsformsdeviation"]      = ByteWord2Int(substr($id3info[$frame_name]["data"], 9, 1));
						$id3info[$frame_name]["data"] = substr($id3info[$frame_name]["data"], 10);
						while ($frame_offset < strlen($id3info[$frame_name]["data"])) {
							$deviationbitstream .= str_pad(decbin(ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1))), 8, "0", STR_PAD_LEFT);
						}
						while (strlen($deviationbitstream)) {
							$id3info[$frame_name][$frame_arrayindex]["bytedeviation"] = substr($deviationbitstream, 0, $id3info[$frame_name]["bitsforbytesdeviation"]);
							$id3info[$frame_name][$frame_arrayindex]["msdeviation"]   = substr($deviationbitstream, $id3info[$frame_name]["bitsforbytesdeviation"], $id3info[$frame_name]["bitsformsdeviation"]);
							$deviationbitstream = substr($deviationbitstream, $id3info[$frame_name]["bitsforbytesdeviation"] + $id3info[$frame_name]["bitsformsdeviation"]);
							$frame_arrayindex++;
						}
						unset($id3info[$frame_name]["data"]);
						

					} else if ($frame_name == "SYTC") { // 4.7   SYTC Synchronised tempo codes
						//   There may only be one "SYTC" frame in each tag
						// <Header for 'Synchronised tempo codes', ID: "SYTC">
						// Time stamp format   $xx
						// Tempo data          <binary data>
						//   Where time stamp format is:
						// $01  (32-bit value) MPEG frames from beginning of file
						// $02  (32-bit value) milliseconds from beginning of file

						$frame_offset = 0;
						$id3info[$frame_name]["timestampformat"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						while ($frame_offset < strlen($id3info[$frame_name]["data"])) {
							$id3info[$frame_name][$frame_arrayindex]["tempo"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
							if ($id3info[$frame_name][$frame_arrayindex]["tempo"] == 255) {
								$id3info[$frame_name][$frame_arrayindex]["tempo"] += ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
							}
							$id3info[$frame_name][$frame_arrayindex]["timestamp"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));
							$frame_offset += 4;
							$frame_arrayindex++;
						}


					} else if ($frame_name == "USLT") { // 4.8   USLT Unsynchronised lyric/text transcription
						//   There may be more than one 'Unsynchronised lyrics/text transcription' frame
						//   in each tag, but only one with the same language and content descriptor.
						// <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT">
						// Text encoding        $xx
						// Language             $xx xx xx
						// Content descriptor   <text string according to encoding> $00 (00)
						// Lyrics/text          <full text string according to encoding>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_language = substr($id3info[$frame_name]["data"], $frame_offset, 3);
						$frame_offset += 3;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = "";
						}
						$id3info[$frame_name][$frame_arrayindex]["encodingid"]  = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]    = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["data"]        = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));
						$id3info[$frame_name][$frame_arrayindex]["flags"]       = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["language"]    = $frame_language;
						$id3info[$frame_name][$frame_arrayindex]["data"]        = $frame_description;
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidata"]   = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_arrayindex]["data"], $frame_textencoding);
						}
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "SYLT") { // 4.9   SYLT Synchronised lyric/text
						//   There may be more than one "SYLT" frame in each tag,
						//   but only one with the same language and content descriptor.
						// <Header for 'Synchronised lyrics/text', ID: "SYLT">
						// Text encoding        $xx
						// Language             $xx xx xx
						// Time stamp format    $xx
						//   $01  (32-bit value) MPEG frames from beginning of file
						//   $02  (32-bit value) milliseconds from beginning of file
						// Content type         $xx
						// Content descriptor   <text string according to encoding> $00 (00)
						//   Terminated text to be synced (typically a syllable)
						//   Sync identifier (terminator to above string)   $00 (00)
						//   Time stamp                                     $xx (xx ...)

						$SYTLContentTypeLookup[hexdec("00")] = "other";
						$SYTLContentTypeLookup[hexdec("01")] = "lyrics";
						$SYTLContentTypeLookup[hexdec("02")] = "text transcription";
						$SYTLContentTypeLookup[hexdec("03")] = "movement/part name";
						$SYTLContentTypeLookup[hexdec("04")] = "events";
						$SYTLContentTypeLookup[hexdec("05")] = "chord";
						$SYTLContentTypeLookup[hexdec("06")] = "trivia/'pop up' information";
						$SYTLContentTypeLookup[hexdec("07")] = "URLs to webpages";
						$SYTLContentTypeLookup[hexdec("08")] = "URLs to images";

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_language = substr($id3info[$frame_name]["data"], $frame_offset, 3);
						$frame_offset += 3;
						$id3info[$frame_name][$frame_arrayindex]["timestampformat"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name][$frame_arrayindex]["contenttypeid"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name][$frame_arrayindex]["contenttype"] = $SYTLContentTypeLookup[$id3info[$frame_name][$frame_arrayindex]["contenttypeid"]];
						
						$timestampindex = 0;
						$frame_remainingdata = substr($id3info[$frame_name]["data"], $frame_offset);
						while (strlen($frame_remainingdata)) {
							$frame_offset = 0;
							$frame_terminatorpos = strpos($frame_remainingdata, $TextEncodingLookup["terminator"][$frame_textencoding]);
							if ($frame_terminatorpos === FALSE) {
								$frame_remainingdata = "";
							} else {
								if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
									$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
								}
								$id3info[$frame_name][$frame_arrayindex]["data"][$timestampindex]["data"] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
								if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
									$id3info[$frame_name][$frame_arrayindex]["data"][$timestampindex]["asciidata"] = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_arrayindex]["data"][$timestampindex]["data"], $frame_textencoding);
								}

								$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));
								if (($timestampindex == 0) && (ord(substr($frame_remainingdata, 0, 1)) != 0)) {
									// timestamp probably omitted for first data item
								} else {
									$id3info[$frame_name][$frame_arrayindex]["data"][$timestampindex]["timestamp"] = ByteWord2Int(substr($frame_remainingdata, 0, 4));
									$frame_remainingdata = substr($frame_remainingdata, 4);
								}
								$timestampindex++;
							}
						}
						
						$id3info[$frame_name][$frame_arrayindex]["encodingid"] = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]   = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["flags"]      = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["language"]   = $frame_language;
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "COMM") { // 4.10  COMM Comments
						//   There may be more than one comment frame in each tag,
						//   but only one with the same language and content descriptor.
						// <Header for 'Comment', ID: "COMM">
						// Text encoding          $xx
						// Language               $xx xx xx
						// Short content descrip. <text string according to encoding> $00 (00)
						// The actual text        <full text string according to encoding>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_language = substr($id3info[$frame_name]["data"], $frame_offset, 3);
						$frame_offset += 3;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = "";
						}
						$frame_text = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));

						$id3info[$frame_name][$frame_arrayindex]["encodingid"]       = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]         = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["flags"]            = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_arrayindex]["language"]         = $frame_language;
						$id3info[$frame_name][$frame_arrayindex]["description"]      = $frame_description;
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidescription"] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding);
						}
						$id3info[$frame_name][$frame_arrayindex]["data"]             = $frame_text;
						if ($id3info[$frame_name][$frame_arrayindex]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_arrayindex]["asciidata"]        = RoughTranslateUnicodeToASCII($frame_text, $frame_textencoding);
						}
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "RVA2") { // 4.11  RVA2 Relative volume adjustment (2)
						//   There may be more than one "RVA2" frame in each tag,
						//   but only one with the same identification string
						// <Header for 'Relative volume adjustment (2)', ID: "RVA2">
						// Identification          <text string> $00
						//   The 'identification' string is used to identify the situation and/or
						//   device where this adjustment should apply. The following is then
						//   repeated for every channel:
						// Type of channel         $xx
						// Volume adjustment       $xx xx
						// Bits representing peak  $xx
						// Peak volume             $xx (xx ...)

						$RVA2ChannelTypeLookup[hexdec("00")] = "Other";
						$RVA2ChannelTypeLookup[hexdec("01")] = "Master volume";
						$RVA2ChannelTypeLookup[hexdec("02")] = "Front right";
						$RVA2ChannelTypeLookup[hexdec("03")] = "Front left";
						$RVA2ChannelTypeLookup[hexdec("04")] = "Back right";
						$RVA2ChannelTypeLookup[hexdec("05")] = "Back left";
						$RVA2ChannelTypeLookup[hexdec("06")] = "Front centre";
						$RVA2ChannelTypeLookup[hexdec("07")] = "Back centre";
						$RVA2ChannelTypeLookup[hexdec("08")] = "Subwoofer";

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0));
						$frame_idstring = substr($id3info[$frame_name]["data"], 0, $frame_terminatorpos);
						if (ord($frame_idstring) === 0) {
							$frame_idstring = 0;
						}
						$frame_remainingdata = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen(chr(0)));
						while (strlen($frame_remainingdata)) {
							$frame_offset = 0;
							$id3info[$frame_name][$frame_idstring]["channeltypeid"]  = substr($frame_remainingdata, $frame_offset++, 1);
							$id3info[$frame_name][$frame_idstring]["channeltype"]    = $RVA2ChannelTypeLookup[$id3info[$frame_name][$frame_idstring]["channeltypeid"]];
							$id3info[$frame_name][$frame_idstring]["volumeadjust"]   = ByteWord2Int(substr($frame_remainingdata, $frame_offset, 2)) - hexdec("7FFF"); // 16-bit signed
							$frame_offset += 2;
							$id3info[$frame_name][$frame_idstring]["bitspeakvolume"] = ord(substr($frame_remainingdata, $frame_offset++, 1));
							$frame_bytespeakvolume = ceil($id3info[$frame_name][$frame_idstring]["bitspeakvolume"] / 8);
							$id3info[$frame_name][$frame_idstring]["peakvolume"]     = ByteWord2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
							$frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
						}
						$id3info[$frame_name][$frame_idstring]["flags"] = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "EQU2") { // 4.12  EQU2 Equalisation (2)
						//   There may be more than one "EQU2" frame in each tag,
						//   but only one with the same identification string
						// <Header of 'Equalisation (2)', ID: "EQU2">
						// Interpolation method  $xx
						//   $00  Band
						//   $01  Linear
						// Identification        <text string> $00
						//   The following is then repeated for every adjustment point
						// Frequency          $xx xx
						// Volume adjustment  $xx xx
						
						$frame_offset = 0;
						$frame_interpolationmethod = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_idstring = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_idstring) === 0) {
							$frame_idstring = 0;
						}
						$frame_remainingdata = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen(chr(0)));
						while (strlen($frame_remainingdata)) {
							$frame_frequency = ByteWord2Int(substr($frame_remainingdata, 0, 2));
							$id3info[$frame_name][$frame_idstring]["data"][$frame_frequency] = ByteWord2Int(substr($frame_remainingdata, 2, 2));
							$frame_remainingdata = substr($frame_remainingdata, 4);
						}
						$id3info[$frame_name][$frame_idstring]["interpolationmethod"] = $frame_interpolationmethod;
						$id3info[$frame_name][$frame_idstring]["flags"] = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "RVRB") { // 4.13  RVRB Reverb
						//   There may only be one "RVRB" frame in each tag.
						// <Header for 'Reverb', ID: "RVRB">
						// Reverb left (ms)                 $xx xx
						// Reverb right (ms)                $xx xx
						// Reverb bounces, left             $xx
						// Reverb bounces, right            $xx
						// Reverb feedback, left to left    $xx
						// Reverb feedback, left to right   $xx
						// Reverb feedback, right to right  $xx
						// Reverb feedback, right to left   $xx
						// Premix left to right             $xx
						// Premix right to left             $xx

						$frame_offset = 0;
						$id3info[$frame_name]["reverb"]["left"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 2));
						$frame_offset += 2;
						$id3info[$frame_name]["reverb"]["right"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 2));
						$frame_offset += 2;
						$id3info[$frame_name]["reverb"]["bouncesL"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["bouncesR"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["feedbackLL"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["feedbackLR"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["feedbackRR"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["feedbackRL"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["premixLR"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["reverb"]["premixRL"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "APIC") { // 4.14  APIC Attached picture
						//   There may be several pictures attached to one file,
						//   each in their individual "APIC" frame, but only one
						//   with the same content descriptor
						// <Header for 'Attached picture', ID: "APIC">
						// Text encoding      $xx
						// MIME type          <text string> $00
						// Picture type       $xx
						// Description        <text string according to encoding> $00 (00)
						// Picture data       <binary data>

						$APICPictureTypeLookup[hexdec("00")] = "Other";
						$APICPictureTypeLookup[hexdec("01")] = "32x32 pixels 'file icon' (PNG only)";
						$APICPictureTypeLookup[hexdec("02")] = "Other file icon";
						$APICPictureTypeLookup[hexdec("03")] = "Cover (front)";
						$APICPictureTypeLookup[hexdec("04")] = "Cover (back)";
						$APICPictureTypeLookup[hexdec("05")] = "Leaflet page";
						$APICPictureTypeLookup[hexdec("06")] = "Media (e.g. label side of CD)";
						$APICPictureTypeLookup[hexdec("07")] = "Lead artist/lead performer/soloist";
						$APICPictureTypeLookup[hexdec("08")] = "Artist/performer";
						$APICPictureTypeLookup[hexdec("09")] = "Conductor";
						$APICPictureTypeLookup[hexdec("0A")] = "Band/Orchestra";
						$APICPictureTypeLookup[hexdec("0B")] = "Composer";
						$APICPictureTypeLookup[hexdec("0C")] = "Lyricist/text writer";
						$APICPictureTypeLookup[hexdec("0D")] = "Recording Location";
						$APICPictureTypeLookup[hexdec("0E")] = "During recording";
						$APICPictureTypeLookup[hexdec("0F")] = "During performance";
						$APICPictureTypeLookup[hexdec("10")] = "Movie/video screen capture";
						$APICPictureTypeLookup[hexdec("11")] = "A bright coloured fish";
						$APICPictureTypeLookup[hexdec("12")] = "Illustration";
						$APICPictureTypeLookup[hexdec("13")] = "Band/artist logotype";
						$APICPictureTypeLookup[hexdec("14")] = "Publisher/Studio logotype";

						$frame_offset = 0;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_mimetype = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_mimetype) === 0) {
							$frame_mimetype = "";
						}
						
						$frame_offset = $frame_terminatorpos + strlen(chr(0));
						$frame_picturetype = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = count($id3info[$frame_name]) - 2;
						}
						$id3info[$frame_name][$frame_description]["flags"]            = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_description]["encodingid"]       = $frame_textencoding;
						$id3info[$frame_name][$frame_description]["encoding"]         = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_description]["mime"]             = $frame_mimetype;
						$id3info[$frame_name][$frame_description]["picturetypeid"]    = $frame_picturetype;
						$id3info[$frame_name][$frame_description]["picturetype"]      = $APICPictureTypeLookup[$frame_picturetype];
						$id3info[$frame_name][$frame_description]["description"]      = $frame_description;
						if ($id3info[$frame_name][$frame_description]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_description]["asciidescription"] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding);
						}
						$id3info[$frame_name][$frame_description]["data"]             = substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]));
						unset($id3info[$frame_name]["data"]);
						unset($id3info[$frame_name]["flags"]);


					} else if ($frame_name == "GEOB") { // 4.15  GEOB General encapsulated object
						//   There may be more than one "GEOB" frame in each tag,
						//   but only one with the same content descriptor
						// <Header for 'General encapsulated object', ID: "GEOB">
						// Text encoding          $xx
						// MIME type              <text string> $00
						// Filename               <text string according to encoding> $00 (00)
						// Content description    <text string according to encoding> $00 (00)
						// Encapsulated object    <binary data>

						$frame_offset = 0;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_mimetype = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_mimetype) === 0) {
							$frame_mimetype = "";
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_filename = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_filename) === 0) {
							$frame_filename = "";
						}
						$frame_offset = $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]);

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = count($id3info[$frame_name]) - 2;
						}
						$frame_offset = $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]);

						$id3info[$frame_name][$frame_description]["objectdata"]       = substr($id3info[$frame_name]["data"], $frame_offset);
						$id3info[$frame_name][$frame_description]["flags"]            = $id3info[$frame_name]["flags"];
						$id3info[$frame_name][$frame_description]["encodingid"]       = $frame_textencoding;
						$id3info[$frame_name][$frame_description]["encoding"]         = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_description]["mime"]             = $frame_mimetype;
						$id3info[$frame_name][$frame_description]["description"]      = $frame_description;
						if ($id3info[$frame_name][$frame_description]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_description]["asciidescription"] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding);
						}
						unset($id3info[$frame_name]["data"]);
						unset($id3info[$frame_name]["flags"]);


					} else if ($frame_name == "PCNT") { // 4.16  PCNT Play counter
						//   There may only be one "PCNT" frame in each tag.
						//   When the counter reaches all one's, one byte is inserted in
						//   front of the counter thus making the counter eight bits bigger
						// <Header for 'Play counter', ID: "PCNT">
						// Counter        $xx xx xx xx (xx ...)

						for ($i=0;$i<strlen($id3info[$frame_name]["data"]);$i++) {
							$frame_hexcounterstring .= str_pad(dechex(ord(substr($id3info[$frame_name]["data"], $i, 1))), 2, "0", STR_PAD_LEFT);
						}
						$id3info[$frame_name]["data"] = bighexdec($frame_hexcounterstring);


					} else if ($frame_name == "POPM") { // 4.17  POPM Popularimeter
						//   There may be more than one "POPM" frame in each tag,
						//   but only one with the same email address
						// <Header for 'Popularimeter', ID: "POPM">
						// Email to user   <text string> $00
						// Rating          $xx
						// Counter         $xx xx xx xx (xx ...)

						$frame_offset = 0;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_emailaddress = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_emailaddress) === 0) {
							$frame_emailaddress = count($id3info[$frame_name]) - 2;
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));
						$frame_rating = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["data"] = substr($id3info[$frame_name]["data"], $frame_offset);
						if (strlen($id3info[$frame_name]["data"]) > 0) {
							for ($i=0;$i<strlen($id3info[$frame_name]["data"]);$i++) {
								$frame_hexcounterstring .= str_pad(dechex(ord(substr($id3info[$frame_name]["data"], $i, 1))), 2, "0", STR_PAD_LEFT);
							}
							$id3info[$frame_name][$frame_emailaddress]["counter"] = bighexdec($frame_hexcounterstring);
						}
						$id3info[$frame_name][$frame_emailaddress]["rating"]  = $frame_rating;
						$id3info[$frame_name][$frame_emailaddress]["flags"]  = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "RBUF") { // 4.18  RBUF Recommended buffer size
						//   There may only be one "RBUF" frame in each tag
						// <Header for 'Recommended buffer size', ID: "RBUF">
						// Buffer size               $xx xx xx
						// Embedded info flag        %0000000x
						// Offset to next tag        $xx xx xx xx

						$frame_offset = 0;
						$id3info[$frame_name]["buffersize"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 3));
						$frame_offset += 3;
						
						$frame_embeddedinfoflags = str_pad(decbin(substr($id3info[$frame_name]["data"], $frame_offset++, 1)), 8, "0", STR_PAD_LEFT);
						$id3info[$frame_name]["flags"]["embededinfo"] = substr($frame_embeddedinfoflags, 7, 1);
						$id3info[$frame_name]["nexttagoffset"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "AENC") { // 4.19  AENC Audio encryption
						//   There may be more than one "AENC" frames in a tag,
						//   but only one with the same 'Owner identifier'
						// <Header for 'Audio encryption', ID: "AENC">
						// Owner identifier   <text string> $00
						// Preview start      $xx xx
						// Preview length     $xx xx
						// Encryption info    <binary data>

						$frame_offset = 0;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_ownerid = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_ownerid) === 0) {
							$frame_ownerid = count($id3info[$frame_name]) - 2;
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));
						$id3info[$frame_name][$frame_ownerid]["previewstart"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 2));
						$frame_offset += 2;
						$id3info[$frame_name][$frame_ownerid]["previewlength"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 2));
						$frame_offset += 2;
						$id3info[$frame_name][$frame_ownerid]["encryptioninfo"] = substr($id3info[$frame_name]["data"], $frame_offset);
						$id3info[$frame_name][$frame_ownerid]["flags"]  = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "LINK") { // 4.20  LINK Linked information
						//   There may be more than one "LINK" frame in a tag,
						//   but only one with the same contents
						// <Header for 'Linked information', ID: "LINK">
						// Frame identifier        $xx xx xx xx
						// URL                     <text string> $00
						// ID and additional data  <text string(s)>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$id3info[$frame_name][$frame_arrayindex]["frameid"] = substr($id3info[$frame_name]["data"], $frame_offset, 4);
						$frame_offset +=4;

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_url = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_url) === 0) {
							$frame_url = "";
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));
						$id3info[$frame_name][$frame_arrayindex]["URL"] = $frame_url;

						$id3info[$frame_name][$frame_arrayindex]["additionaldata"] = substr($id3info[$frame_name]["data"], $frame_offset);
						$id3info[$frame_name][$frame_arrayindex]["flags"]  = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "POSS") { // 4.21  POSS Position synchronisation frame
						//   There may only be one "POSS" frame in each tag
						// <Head for 'Position synchronisation', ID: "POSS">
						// Time stamp format         $xx
						// Position                  $xx (xx ...)

						$frame_offset = 0;
						$id3info[$frame_name]["timestampformat"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["position"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset));
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "USER") { // 4.22  USER Terms of use
						//   There may be more than one 'Terms of use' frame in a tag,
						//   but only one with the same 'Language'
						// <Header for 'Terms of use frame', ID: "USER">
						// Text encoding        $xx
						// Language             $xx xx xx
						// The actual text      <text string according to encoding>

						$frame_offset = 0;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$frame_language = ord(substr($id3info[$frame_name]["data"], $frame_offset, 3));
						$frame_offset += 3;
						$id3info[$frame_name][$frame_language]["encodingid"] = $frame_textencoding;
						$id3info[$frame_name][$frame_language]["encoding"]   = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_language]["data"]       = substr($id3info[$frame_name]["data"], $frame_offset);
						if ($id3info[$frame_name][$frame_language]["flags"]["Compression"] != "1") {
							$id3info[$frame_name][$frame_language]["asciidata"] = RoughTranslateUnicodeToASCII($id3info[$frame_name][$frame_language]["data"], $frame_textencoding);
						}
						$id3info[$frame_name][$frame_language]["flags"]  = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "OWNE") { // 4.23  OWNE Ownership frame
						//   There may only be one "OWNE" frame in a tag
						// <Header for 'Ownership frame', ID: "OWNE">
						// Text encoding     $xx
						// Price paid        <text string> $00
						// Date of purch.    <text string>
						// Seller            <text string according to encoding>

						$frame_offset = 0;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name]["encodingid"] = $frame_textencoding;
						$id3info[$frame_name]["encoding"]   = $TextEncodingLookup["encoding"][$frame_textencoding];

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_pricepaid = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$id3info[$frame_name]["pricepaid"]["currencyid"] = ord(substr($frame_pricepaid, 0, 3));
						$id3info[$frame_name]["pricepaid"]["currency"]   = LookupCurrency($id3info[$frame_name]["pricepaid"]["currencyid"]);
						$id3info[$frame_name]["pricepaid"]["value"]      = ord(substr($frame_pricepaid, 3));

						$id3info[$frame_name]["purchasedate"] = ord(substr($id3info[$frame_name]["data"], $frame_offset, 8));
						$frame_offset += 8;

						$id3info[$frame_name]["seller"] = substr($id3info[$frame_name]["data"], $frame_offset);

						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "COMR") { // 4.24  COMR Commercial frame
						//   There may be more than one 'commercial frame' in a tag,
						//   but no two may be identical
						// <Header for 'Commercial frame', ID: "COMR">
						// Text encoding      $xx
						// Price string       <text string> $00
						// Valid until        <text string>
						// Contact URL        <text string> $00
						// Received as        $xx
						// Name of seller     <text string according to encoding> $00 (00)
						// Description        <text string according to encoding> $00 (00)
						// Picture MIME type  <string> $00
						// Seller logo        <binary data>

						$COMRReceivedAsLookup[hexdec("00")] = "Other";
						$COMRReceivedAsLookup[hexdec("01")] = "Standard CD album with other songs";
						$COMRReceivedAsLookup[hexdec("02")] = "Compressed audio on CD";
						$COMRReceivedAsLookup[hexdec("03")] = "File over the Internet";
						$COMRReceivedAsLookup[hexdec("04")] = "Stream over the Internet";
						$COMRReceivedAsLookup[hexdec("05")] = "As note sheets";
						$COMRReceivedAsLookup[hexdec("06")] = "As note sheets in a book with other sheets";
						$COMRReceivedAsLookup[hexdec("07")] = "Music on other media";
						$COMRReceivedAsLookup[hexdec("08")] = "Non-musical merchandise";

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_textencoding = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_pricestring = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						$frame_offset = $frame_terminatorpos + strlen(chr(0));
						$frame_rawpricearray = explode("/", $frame_pricestring);
						reset($frame_rawpricearray);
						while (list($key, $val) = each($frame_rawpricearray)) {
							$frame_currencyid = ord(substr($val, 0, 3));
							$id3info[$frame_name][$frame_arrayindex]["price"][$frame_currencyid]["currency"] = LookupCurrency($frame_currencyid);
							$id3info[$frame_name][$frame_arrayindex]["price"][$frame_currencyid]["value"]    = ord(substr($val, 3));
						}

						$frame_datestring = substr($id3info[$frame_name]["data"], $frame_offset, 8);
						$frame_offset += 8;

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_contacturl = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$frame_receivedasid = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_sellername = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_sellername) === 0) {
							$frame_sellername = "";
						}
						$frame_offset = $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]);

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], $TextEncodingLookup["terminator"][$frame_textencoding], $frame_offset);
						if (ord(substr($id3info[$frame_name]["data"], $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]), 1)) === 0) {
							$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
						}
						$frame_description = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_description) === 0) {
							$frame_description = "";
						}
						$frame_offset = $frame_terminatorpos + strlen($TextEncodingLookup["terminator"][$frame_textencoding]);

						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_mimetype = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$frame_sellerlogo = substr($id3info[$frame_name]["data"], $frame_offset);

						$id3info[$frame_name][$frame_arrayindex]["encodingid"]        = $frame_textencoding;
						$id3info[$frame_name][$frame_arrayindex]["encoding"]          = $TextEncodingLookup["encoding"][$frame_textencoding];
						$id3info[$frame_name][$frame_arrayindex]["pricevaliduntil"]   = $frame_datestring;
						$id3info[$frame_name][$frame_arrayindex]["contacturl"]        = $frame_contacturl;
						$id3info[$frame_name][$frame_arrayindex]["receivedasid"]      = $frame_receivedasid;
						$id3info[$frame_name][$frame_arrayindex]["receivedas"]        = $COMRReceivedAsLookup[$frame_receivedasid];
						$id3info[$frame_name][$frame_arrayindex]["sellername"]        = $frame_sellername;
						$id3info[$frame_name][$frame_arrayindex]["asciisellername"]   = RoughTranslateUnicodeToASCII($frame_sellername, $frame_textencoding);
						$id3info[$frame_name][$frame_arrayindex]["description"]       = $frame_description;
						$id3info[$frame_name][$frame_arrayindex]["asciidescription"]  = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding);
						$id3info[$frame_name][$frame_arrayindex]["mime"]              = $frame_mimetype;
						$id3info[$frame_name][$frame_arrayindex]["logo"]              = $frame_sellerlogo;
						$id3info[$frame_name][$frame_arrayindex]["flags"]             = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "ENCR") { // 4.25  ENCR Encryption method registration
						//   There may be several "ENCR" frames in a tag,
						//   but only one containing the same symbol
						//   and only one containing the same owner identifier
						// <Header for 'Encryption method registration', ID: "ENCR">
						// Owner identifier    <text string> $00
						// Method symbol       $xx
						// Encryption data     <binary data>
						
						$frame_offset = 0;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_ownerid = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_ownerid) === 0) {
							$frame_ownerid = count($id3info[$frame_name]) - 2;
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$id3info[$frame_name][$frame_ownerid]["methodsymbol"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name][$frame_ownerid]["data"]         = ord(substr($id3info[$frame_name]["data"], $frame_offset));
						$id3info[$frame_name][$frame_ownerid]["flags"]        = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);
						
					
					} else if ($frame_name == "GRID") { // 4.26  GRID Group identification registration

						//   There may be several "GRID" frames in a tag,
						//   but only one containing the same symbol
						//   and only one containing the same owner identifier
						// <Header for 'Group ID registration', ID: "GRID">
						// Owner identifier      <text string> $00
						// Group symbol          $xx
						// Group dependent data  <binary data>

						$frame_offset = 0;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_ownerid = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_ownerid) === 0) {
							$frame_ownerid = count($id3info[$frame_name]) - 2;
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$id3info[$frame_name][$frame_ownerid]["groupsymbol"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name][$frame_ownerid]["data"]        = ord(substr($id3info[$frame_name]["data"], $frame_offset));
						$id3info[$frame_name][$frame_ownerid]["flags"]       = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);
						
					
					} else if ($frame_name == "PRIV") { // 4.27  PRIV Private frame
						//   The tag may contain more than one "PRIV" frame
						//   but only with different contents
						// <Header for 'Private frame', ID: "PRIV">
						// Owner identifier      <text string> $00
						// The private data      <binary data>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$frame_terminatorpos = strpos($id3info[$frame_name]["data"], chr(0), $frame_offset);
						$frame_ownerid = substr($id3info[$frame_name]["data"], $frame_offset, $frame_terminatorpos - $frame_offset);
						if (ord($frame_ownerid) === 0) {
							$frame_ownerid = "";
						}
						$frame_offset = $frame_terminatorpos + strlen(chr(0));

						$id3info[$frame_name][$frame_arrayindex]["ownerid"] = $frame_ownerid;
						$id3info[$frame_name][$frame_arrayindex]["data"]    = ord(substr($id3info[$frame_name]["data"], $frame_offset));
						$id3info[$frame_name][$frame_arrayindex]["flags"]   = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);
						
					
					} else if ($frame_name == "SIGN") { // 4.28  SIGN Signature frame
						//   There may be more than one 'signature frame' in a tag,
						//   but no two may be identical
						// <Header for 'Signature frame', ID: "SIGN">
						// Group symbol      $xx
						// Signature         <binary data>

						$frame_offset = 0;
						$frame_arrayindex = count($id3info[$frame_name]) - 2;
						$id3info[$frame_name][$frame_arrayindex]["groupsymbol"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 1));
						$id3info[$frame_name][$frame_arrayindex]["data"]        = ord(substr($id3info[$frame_name]["data"], $frame_offset));
						$id3info[$frame_name][$frame_arrayindex]["flags"]       = $id3info[$frame_name]["flags"];
						unset($id3info[$frame_name]["flags"]);
						unset($id3info[$frame_name]["data"]);


					} else if ($frame_name == "SEEK") { // 4.29  SEEK Seek frame
						//   There may only be one 'seek frame' in a tag
						// <Header for 'Seek frame', ID: "SEEK">
						// Minimum offset to next tag       $xx xx xx xx

						$frame_offset = 0;
						$id3info[$frame_name]["data"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));


					} else if ($frame_name == "ASPI") { // 4.30  ASPI Audio seek point index
						//   There may only be one 'audio seek point index' frame in a tag
						// <Header for 'Seek Point Index', ID: "ASPI">
						// Indexed data start (S)         $xx xx xx xx
						// Indexed data length (L)        $xx xx xx xx
						// Number of index points (N)     $xx xx
						// Bits per index point (b)       $xx
						//   Then for every index point the following data is included:
						// Fraction at index (Fi)          $xx (xx)
						
						$frame_offset = 0;
						$id3info[$frame_name]["datastart"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));
						$frame_offset += 4;
						$id3info[$frame_name]["datalength"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 4));
						$frame_offset += 4;
						$id3info[$frame_name]["indexpoints"] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, 2));
						$frame_offset += 2;
						$id3info[$frame_name]["bitsperpoint"] = ord(substr($id3info[$frame_name]["data"], $frame_offset++, 2));
						$frame_bytesperpoint = $id3info[$frame_name]["bitsperpoint"] / 8;
						for ($i=0;$i<$frame_indexpoints;$i++) {
							$id3info[$frame_name]["indexes"][$i] = ByteWord2Int(substr($id3info[$frame_name]["data"], $frame_offset, $frame_bytesperpoint));
							$frame_offset += $frame_bytesperpoint;
						}

						unset($id3info[$frame_name]["data"]);

					}


				} else { // invalid frame length or FrameID
					$id3info["error"] .= "\nerror parsing \"".$frame_name."\" (".($id3info["headerlength"] - strlen($framedata))." bytes into the ID3v2 tag).";
					if ($frame_size > strlen($framedata)){
						$id3info["error"] .= " (ERROR: \$frame_size (".$frame_size.") > strlen(\$framedata) (".strlen($framedata).")).";
					}
					if (!IsValidID3v2FrameName($frame_name)) {
						$id3info["error"] .= " (ERROR: !IsValidID3v2FrameName(\$frame_name (".str_replace(chr(0), " ", $frame_name)."))).";
					}
					if (($frame_name == chr(0)."MP3") || ($frame_name == " MP3")) {
						$id3info["error"] .= " [Note: this particular error has been known to happen with tags edited by \"MP3ext V3.3.17(unicode)\"]";
					}
					unset($framedata);
				}
			} else { // <= ID3v2.2.0
				// ID3v2.2 declares frames differently:
				// The headers of the frames are similar in their construction.
				// They consist of one three character identifier (capital A-Z and 0-9)
				// and one three byte size field, making a total of six bytes.
				// The three character frame identifier is followed by a three byte size
				// descriptor, making a total header size of six bytes in every frame.
				// The size is calculated as framesize excluding frame identifier and
				// size descriptor (frame size - 6).
				
				$frame_header = substr($framedata, 0, 6); // take next 10 bytes for header
				$framedata = substr($framedata, 6);       // and leave the rest in $framedata

				$frame_name = substr($frame_header, 0, 3);
				if ((ord(substr($frame_name, 0, 1)) == 0) &&
					(ord(substr($frame_name, 1, 1)) == 0) &&
					(ord(substr($frame_name, 2, 1)) == 0)) {
						break; // padding encountered, skip rest of ID3v2 header
				}

				$frame_rawsize = substr($frame_header, 3, 3);

				$framesize1 = str_pad(decbin(ord(substr($frame_rawsize, 0, 1))), 8, "0", STR_PAD_LEFT);
				$framesize2 = str_pad(decbin(ord(substr($frame_rawsize, 1, 1))), 8, "0", STR_PAD_LEFT);
				$framesize3 = str_pad(decbin(ord(substr($frame_rawsize, 2, 1))), 8, "0", STR_PAD_LEFT);
				$frame_size = bindec($framesize1.$framesize2.$framesize3);

				if (($frame_size <= strlen($framedata)) &&	(IsValidID3v22FrameName($frame_name))) {
					$id3info[$frame_name]["data"]  = substr($framedata, 0, $frame_size);
					$framedata = substr($framedata, $frame_size);
				} else {
					$id3info["error"] .= "\nerror parsing \"".$frame_name."\" (".($id3info["headerlength"] + 6 - strlen($framedata))." bytes into the ID3v2 tag)";
					unset($framedata);
				}

			}
		}


//	Footer

	//	To speed up the process of locating an ID3v2 tag when searching from
	//	the end of a file, a footer can be added to the tag. It is REQUIRED
	//	to add a footer to an appended tag, i.e. a tag located after all
	//	audio data. The footer is a copy of the header, but with a different
	//	identifier.
	//		ID3v2 identifier           "3DI"
	//		ID3v2 version              $04 00
	//		ID3v2 flags                %abcd0000
	//		ID3v2 size             4 * %0xxxxxxx

		if ($id3info["flags"]["isfooter"]) {
			$footer = fread ($fd, 10);
			$id3_identifier = substr($footer, 0, 3);
			if ($id3_identifier == "3DI") {
				$id3info["footer"] = true;
				$id3info["majorversion_footer"] = ord(substr($footer, 3, 1));
				$id3info["minorversion_footer"] = ord(substr($footer, 4, 1));
			}
			if ($id3info["majorversion_footer"] <= 4) {
				$id3_flags = str_pad(decbin(ord(substr($footer, 5, 1))), 8, "0", STR_PAD_LEFT);
				$id3info["flags"]["unsynch_footer"]  = substr($id3_flags, 0, 1);
				$id3info["flags"]["extfoot_footer"]  = substr($id3_flags, 1, 1);
				$id3info["flags"]["experim_footer"]  = substr($id3_flags, 2, 1);
				$id3info["flags"]["isfooter_footer"] = substr($id3_flags, 3, 1);

				$id3_footerlength1 = substr(str_pad(decbin(ord(substr($footer, 6, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$id3_footerlength2 = substr(str_pad(decbin(ord(substr($footer, 7, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$id3_footerlength3 = substr(str_pad(decbin(ord(substr($footer, 8, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$id3_footerlength4 = substr(str_pad(decbin(ord(substr($footer, 9, 1))), 8, "0", STR_PAD_LEFT), 1, 7);
				$id3info["footerlength"] = bindec($id3_footerlength1.$id3_footerlength2.$id3_footerlength3.$id3_footerlength4);
			}
		} // end footer
	} else { // if ($id3info["majorversion"] <= 4)
		if ($id3info["header"]) {
			$id3info["error"] .= "\nthis script only parses up to ID3v2.4.x - this tag is ID3v2.".$id3info["majorversion"].".".$id3info["minorversion"];
		}
	}

	// Translate most common ID3v2 FrameIDs to easier-to-understand names
	if ($id3info["TIT2"]) { $id3info["title"]   = $id3info["TIT2"]["data"]; }
	if ($id3info["TPE1"]) { $id3info["artist"]  = $id3info["TPE1"]["data"]; }
	if ($id3info["TALB"]) { $id3info["album"]   = $id3info["TALB"]["data"]; }
	if ($id3info["TYER"]) { $id3info["year"]    = $id3info["TYER"]["data"]; }
	if ($id3info["TRCK"]) { $id3info["track"]   = $id3info["TRCK"]["data"]; }
	if ($id3info["TCON"]) { $id3info["genre"]   = $id3info["TCON"]["data"]; }
	if ($id3info["COMM"][0]["asciidata"]) {
		$id3info["comment"] = $id3info["COMM"][0]["asciidata"];
	}

	return $id3info;
}

function decodeheader($headerstring) {
	$byte1 = str_pad(decbin(ord(substr($headerstring, 0, 1))), 8, "0", STR_PAD_LEFT);
	$byte2 = str_pad(decbin(ord(substr($headerstring, 1, 1))), 8, "0", STR_PAD_LEFT);
	$byte3 = str_pad(decbin(ord(substr($headerstring, 2, 1))), 8, "0", STR_PAD_LEFT);
	$byte4 = str_pad(decbin(ord(substr($headerstring, 3, 1))), 8, "0", STR_PAD_LEFT);
	
	$info["version"]       = substr($byte2, 3, 2);
	$info["layer"]         = substr($byte2, 5, 2);
	$info["protection"]    = substr($byte2, 7, 1);
	$info["bitrate"]       = substr($byte3, 0, 4);
	$info["frequency"]     = substr($byte3, 4, 2);
	$info["padding"]       = substr($byte3, 6, 1);
	$info["private"]       = substr($byte3, 7, 1);
	$info["channelmode"]   = substr($byte4, 0, 2);
	$info["modeextension"] = substr($byte4, 2, 2);
	$info["copyright"]     = substr($byte4, 4, 1);
	$info["original"]      = substr($byte4, 5, 1);
	$info["emphasis"]      = substr($byte4, 6, 2);

	$VersionLookup = array("2.5", "", "2", "1");
	$info["version"] = $VersionLookup[bindec($info["version"])];
	
	$LayerLookup = array("", "III", "II", "I");
	$info["layer"] = $LayerLookup[bindec($info["layer"])];
	
	$BitrateLookup["1"]["I"] = array("free", "32", "64", "96", "128", "160", "192", "224", "256", "288", "320", "352", "384", "416", "448");
	$BitrateLookup["1"]["II"] = array("free", "32", "48", "56", "64", "80", "96", "112", "128", "160", "192", "224", "256", "320", "384");
	$BitrateLookup["1"]["III"] = array("free", "32", "40", "48", "56", "64", "80", "96", "112", "128", "160", "192", "224", "256", "320");
	$BitrateLookup["2"]["I"] = array("free", "32", "48", "56", "64", "80", "96", "112", "128", "144", "160", "176", "192", "224", "256");
	$BitrateLookup["2.5"]["I"] = $BitrateLookup["2"]["I"];
	$BitrateLookup["2"]["II"] = array("free", "8", "16", "24", "32", "40", "48", "56", "64", "80", "96", "112", "128", "144", "160");
	$BitrateLookup["2"]["III"] = $BitrateLookup["2"]["II"];
	$BitrateLookup["2.5"]["II"] = $BitrateLookup["2"]["II"];
	$BitrateLookup["2.5"]["III"] = $BitrateLookup["2"]["II"];
	$info["bitrate"] = $BitrateLookup[$info["version"]][$info["layer"]][bindec($info["bitrate"])];

	$FrequencyLookup["1"] = array("44100", "48000", "32000", "");
	$FrequencyLookup["2"] = array("22050", "24000", "16000", "");
	$FrequencyLookup["2.5"] = array("11025", "12000", "8000", "");
	$info["frequency"] = $FrequencyLookup[$info["version"]][bindec($info["frequency"])];
	
	$ChannelModeLookup = array("stereo", "joint stereo", "dual channel", "mono");
	$info["channelmode"] = $ChannelModeLookup[bindec($info["channelmode"])];
	
	$ModeExtensionLookup["I"] = array("4-31", "8-31", "12-31", "16-31");
	$ModeExtensionLookup["II"] = $ModeExtensionLookup["I"];
	$ModeExtensionLookup["III"] = array("", "IS", "MS", "IS+MS");
	$info["modeextension"] = $ModeExtensionLookup[$info["layer"]][bindec($info["modeextension"])];
	
	$EmphasisLookup = array("none", "50/15ms", "", "CCIT J.17");
	$info["emphasis"] = $EmphasisLookup[bindec($info["emphasis"])];

	// For Layer II there are some combinations of bitrate and mode which are not allowed.
	if ($info["layer"] == "II") {
		switch ($info["channelmode"]) {
			case "mono":
				if ($info["bitrate"] == "free") {
					break;
				}
				if ($info["bitrate"] <= 192) {
					break;
				}
				$info["error"] .= "\n".$info["bitrate"]."kbps not allowed in Layer II, ".$info["channelmode"].".";
				break;
			case "stereo":
			case "joint stereo":
			case "dual channel":
				if ($info["bitrate"] == "free") {
					break;
				}
				if ($info["bitrate"] == 64) {
					break;
				}
				if ($info["bitrate"] >= 96) {
					break;
				}
				$info["error"] .= "\n".$info["bitrate"]."kbps not allowed in Layer II, ".$info["channelmode"].".";
				break;
		}
	}

////////////////////////////////////////////////////////////////////////////////////
	// Variable-bitrate headers

	if ($info["version"] == "1") {
		if ($info["channelmode"] == "mono") {
			$VBRidOffset = (17 + 4);
		} else {
			$VBRidOffset = (32 + 4);
		}
	} else { // 2 or 2.5
		if ($info["channelmode"] == "mono") {
			$VBRidOffset = (9 + 4);
		} else {
			$VBRidOffset = (17 + 4);
		}
	}
	$VBRid = substr($headerstring, $VBRidOffset, 4);
	if ($VBRid == "Xing") {
		$info["bitratemode"]   = "VBR";
		$info["VBR_method"] = "Xing";
	} else if ($VBRid == "VBRI") {
		$info["bitratemode"]   = "VBR";
		$info["VBR_method"] = "Fraunhofer";
	} else {
		$info["bitratemode"] = "CBR";
	}
	if ($info["VBR_method"] == "Xing") {
		$XingVBROffset = $VBRidOffset + 4;
		$XingHeader_Flags = substr($headerstring, $XingVBROffset, 4);
		$XingVBROffset += 4;
		$XingHeader_byte4 = str_pad(decbin(ord(substr($XingHeader_Flags, 3, 1))), 8, "0", STR_PAD_LEFT);
		$XingHeader_flags["frames"]    = substr($XingHeader_byte4, 4, 1);
		$XingHeader_flags["bytes"]     = substr($XingHeader_byte4, 5, 1);
		$XingHeader_flags["toc"]       = substr($XingHeader_byte4, 6, 1);
		$XingHeader_flags["vbr_scale"] = substr($XingHeader_byte4, 7, 1);
		if ($XingHeader_flags["frames"] == "1") {
			$XingHeader_Frames = substr($headerstring, $XingVBROffset, 4);
			$XingVBROffset += 4;
			$info["VBR_frames"] = ByteWord2Int($XingHeader_Frames);
		}
		if ($XingHeader_flags["bytes"] == "1") {
			$XingHeader_Bytes = substr($headerstring, $XingVBROffset, 4);
			$XingVBROffset += 4;
			$info["VBR_bytes"] = ByteWord2Int($XingHeader_Bytes);
		}
	} else if ($info["VBR_method"] == "Fraunhofer") {
		// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
		$FraunhoferVBROffset = $VBRidOffset + 4;
		$Fraunhofer_version = substr($headerstring, $FraunhoferVBROffset, 4);
		$FraunhoferVBROffset += 4;

		$Fraunhofer_quality = substr($headerstring, $FraunhoferVBROffset, 2);
		$FraunhoferVBROffset += 2;
		$info["VBR_quality"] = ByteWord2Int($Fraunhofer_quality);

		$Fraunhofer_Bytes = substr($headerstring, $FraunhoferVBROffset, 4);
		$FraunhoferVBROffset += 4;
		$info["VBR_bytes"] = ByteWord2Int($Fraunhofer_Bytes);

		$Fraunhofer_Frames = substr($headerstring, $FraunhoferVBROffset, 4);
		$FraunhoferVBROffset += 4;
		$info["VBR_frames"] = ByteWord2Int($Fraunhofer_Frames);
	}
	if ($info["bitratemode"] == "VBR") {
		$info["VBR_frames"]--; // don't count the Xing / VBRI frame
		if (($info["version"] == "1") && ($info["layer"] == "I")) {
			$info["VBR_bitrate"] = ((($info["VBR_bytes"] / $info["VBR_frames"]) * 8) * ($info["frequency"] / 384)) / 1000;
		} else if ((($info["version"] == "2") || ($info["version"] == "2.5")) && ($info["layer"] == "III")) {
			$info["VBR_bitrate"] = ((($info["VBR_bytes"] / $info["VBR_frames"]) * 8) * ($info["frequency"] / 576)) / 1000;
		} else {
			$info["VBR_bitrate"] = ((($info["VBR_bytes"] / $info["VBR_frames"]) * 8) * ($info["frequency"] / 1152)) / 1000;
		}
		$info["bitrate"] = round($info["VBR_bitrate"]);
	}

	return $info;
}

function ByteWord2Int($byteword) {
	for ($i=0;$i<strlen($byteword);$i++) {
		$intvalue <<= 8;
		$intvalue |= ord(substr($byteword, $i, 1));
	}
	return $intvalue;
}

function getMP3header($filename) {
	$fd = fopen($filename, "rb");
	return getMP3headerFilepointer($fd);
}

function getMP3headerFilepointer($fd) {
	if (!$fd) {
		$mp3info["error"] .= "\nCould not open file";
	} else {
		fseek($fd, -128, SEEK_END);
		$id3_tag = fread ($fd, 3);
		if ($id3_tag == "TAG") {
			$mp3info["id3"]["id3v1"] = getID3v1Filepointer($fd);
		}
		rewind($fd);
		$header = fread($fd, 3);
		if (substr($header, 0, 3) == "ID3") {
			$mp3info["id3"]["id3v2"] = getID3v2Filepointer($fd);
			$seekoffset = $mp3info["id3"]["id3v2"]["headerlength"];
			if ($mp3info["id3"]["id3v2"]["header"]) {
				$seekoffset += 10;
			}
			if ($mp3info["id3"]["id3v2"]["footer"]) {
				$seekoffset += 10;
			}
			fseek($fd, $seekoffset);
		} else { // no ID3v2 header
			rewind($fd);
		}
		$header = fread($fd, 256);
		if (substr(decbin(ord(substr($header, 0, 1))).decbin(ord(substr($header, 1, 1))), 0, 11) == "11111111111") { // synch detected
			$decodedheader = decodeheader($header);
			while (list ($key, $val) = each($decodedheader)) {
				$mp3info[$key] .= $val;
			}
			$mp3info["format"] = "mp3";
		} else {
			$mp3info["error"] .= "\nSynch not found";
		}
	} // if ($fd)
	
	return $mp3info;
}

function GetAllMP3info($filename) {
	$filename = rawurldecode($filename);
	$fp = @fopen($filename, "rb"); 
	$MP3fileInfo["exist"] = (bool) $fp;
	$MP3fileInfo["getID3version"] = GETID3VERSION;

	if ($MP3fileInfo["exist"]) {
		if ((strpos($filename, "http://") !== FALSE) || (strpos($filename, "ftp://") !== FALSE)) {
			// remote file - copy locally first and work from there
			$localfilepointer = tmpfile();
			while ($buffer = fread($fp, 4096)) {
				$MP3fileInfo["filesize"] += fwrite($localfilepointer, $buffer);
			}
			fclose($fp);
		} else {
			$MP3fileInfo["filesize"] = filesize($filename);
			$localfilepointer = $fp;
		}
		rewind($localfilepointer);
		$ziptest = fread($localfilepointer, 2);
		if ($ziptest == "PK") {
			$MP3fileInfo["fileformat"] .= "zip";
		} else {
			rewind($localfilepointer);
		
			$mp3info = getMP3headerFilepointer($localfilepointer);
			$MP3fileInfo = array_merge($MP3fileInfo, $mp3info);
			
			if ($MP3fileInfo["bitrate"] > 0) {
				$MP3fileInfo["audiobytes"] = $MP3fileInfo["filesize"];
				$MP3fileInfo["audiobytes"] -= $MP3fileInfo["id3v2"]["headerlength"];
				if ($MP3fileInfo["id3v2"]["header"]) {
					$MP3fileInfo["audiobytes"] -= 10;
				}
				if ($MP3fileInfo["id3v2"]["footer"]) {
					$MP3fileInfo["audiobytes"] -= 10;
				}
				if ($MP3fileInfo["id3v1"]) {
					$MP3fileInfo["audiobytes"] -= 128;
				}
				if ($info["VBR_bitrate"]) {
					$MP3fileInfo["playtime_seconds"] = ($MP3fileInfo["audiobytes"] * 8) / (1000 * $MP3fileInfo["VBR_bitrate"]);
				} else {
					$MP3fileInfo["playtime_seconds"] = ($MP3fileInfo["audiobytes"] * 8) / (1000 * $MP3fileInfo["bitrate"]);
				}
				$contentseconds = round((($MP3fileInfo["playtime_seconds"] / 60) - floor($MP3fileInfo["playtime_seconds"] / 60)) * 60);
				$contentminutes = floor($MP3fileInfo["playtime_seconds"] / 60);
				$MP3fileInfo["playtime_string"]  = number_format($contentminutes).":".str_pad($contentseconds, 2, 0, STR_PAD_LEFT);
			}
		}
	}
	if (is_resource($fp) && (get_resource_type($fp) == "file")) {
		fclose($fp);
	}
	if (is_resource($localfilepointer) && (get_resource_type($localfilepointer) == "file")) {
		fclose($localfilepointer);
	}
	if (isset($fp)) {
		unset($fp);
	}
	if (isset($localfilepointer)) {
		unset($localfilepointer);
	}
 	return $MP3fileInfo;
}

?>