<?php

/*
=====================================================
 Volkov NET!! - by SaNcHeS Group 
-----------------------------------------------------
 http://AllForCS.RU/
-----------------------------------------------------
 Copyright (c) 2009 SaNcHeS Group
=====================================================
 Äàííûé êîä çàùèùåí àâòîðñêèìè ïðàâàìè
=====================================================
 Ôàéë: id3v2.php
-----------------------------------------------------
 Íàçíà÷åíèå: Ïðîâåðêà òåãîâ
=====================================================
*/

################################################################################
#
# ID3v2.php
# Lesen von ID3v1/1.1
#			ID3v2.2
#			ID3v2.3 und MPEG-Frame-Header-Informationen
#
#
# Copyright © 2002-2003 Michael Zeising <software@michaels-website.de>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# LGPL: http://www.gnu.org/copyleft/lesser.html
#
#
# [ANWENDUNG]
# ...
# $id3 = new id3($filename);
#
# $artist = $id3->artist();
# $title = $id3->title();
# ...
#
# [REQUIREMENTS]
# PHP 4.0.1 oder höher
#
# [CHANGELOG]
# 0.1.1		title_from_filename()	hinzugefügt
#			artist_from_filename()	hinzugefügt
# 0.1.2		_read_id3()				Fehler bei der Auswertung der ID3v2-Version
#									behoben
# 0.1.3		year()					Fehlerhafte Werte bei leerem Jahr-Tag
#									ausgeschlossen
# 0.1.4		_read_id3()				Wert für _frame_offset korrigiert
#
# [TODO]
# o bitrate()			Unterstützung für VBR umsetzen
# o _id3v2_frame()		Verschlüsselung und Kompression in ID3v2.4-Frames
#						behandeln
# o _parse_text_frame()	Unicode Textcodierung der Frames unterstützen
#
################################################################################

$id3_version = '0.1.4';

# ID3v2.2-FrameIDs
$v3_v2 = array('TPE1' => 'TP1',
			   'TIT2' => 'TT2',
			   'TALB' => 'TAL',
			   'TCON' => 'TCO',
			   'TYER' => 'TYE',
			   'TRCK' => 'TRK',
			   'COMM' => 'COM');

class id3 {
	var $_lastfile;
	var $_filename;
	var $_error;

	var $_id3v1;				# ID3v1/1.1-Tag (128 Bytes)
	var $_id3v1_included;		# Gibt an, ob ein ID3v1-Tag enthalten ist

	var $_id3v2;
	var $_id3v2_header;			# Kopf des ID3v2-Tags (10 Bytes)
	var $_id3v2_included;		# Gibt an, ob ein ID3v2-Tag enthalten ist
	var $_id3v2_major;			# ID3v2 Version
	var $_id3v2_minor;			# ID3v2 Release

	var $_frame_found;			# Gibt an, ob ein MPEG-Frame in der Datei
								# gefunden wurde
	var $_frame_offset;			# Offset des ersten MPEG-Frames
	var $_frame_header;			# Kopf des ersten MPEG-Frames (4 Bytes/32 Bits)

	############################################################################
	# Konstruktorfunktion
	# VOID id3(STRING filename)
	function id3($filename) {
		$this->_error = '';
		$this->_filename = $filename;
	}

	############################################################################
	# Version der Klasse zurückgeben
	# STRING version()
	function version() {
		return $GLOBALS[id3_version];
	}

	############################################################################
	# Künstler vom Dateinamen ableiten (Künstler - Titel.mp3)
	# STRING artist_from_filename()
	function artist_from_filename() {
		$filename = basename($this->_filename);
		# .mp3-Endung entfernen
		$filename = substr($filename, 0, strlen($filename) - 4);
		# Unterstriche durch Leerzeichen ersetzen
		$filename = str_replace('_', ' ',$filename);

		$artist = substr($filename, 0, strpos($filename, '-'));
		$artist = trim($artist);

		return $artist;
	}

	############################################################################
	# Titel vom Dateinamen ableiten (Künstler - Titel.mp3)
	# STRING title_from_filename()
	function title_from_filename() {
		$filename = basename($this->_filename);
		# .mp3-Endung entfernen
		$filename = substr($filename, 0, strlen($filename) - 4);
		# Unterstriche durch Leerzeichen ersetzen
		$filename = str_replace('_', ' ',$filename);

		$title = substr($filename, strpos($filename, '-') + 1, strlen($filename) - strpos($filename, '-'));
		$title = trim($title);

		return $title;
	}

	############################################################################
	# ID3v1-Tags, ID3v2-Tags und MPEG-Frame-Header lesen
	# BOOL _read_id3()
	function _read_id3() {

		if ($this->_lastfile != $this->_filename) {
			$this->_lastfile = $this->_filename;

			if (!file_exists($this->_filename)) {
				$this->_error = 'File not found'; return false;
			}

			if (!is_readable($this->_filename)) {
				$this->_error = 'File not readable'; return false;
			}

			$fp = fopen($this->_filename, "r");

			$this->_id3v2_header = fread($fp, 10);					# ID3v2

			if (substr($this->_id3v2_header, 0, 3) == 'ID3') {
				$this->_id3v2_included = true;

				$tag_size = big_endian_to_int(substr($this->_id3v2_header, 6, 4), true);

				$this->_id3v2 = fread($fp, ($tag_size - 10));
			}

			rewind($fp);

			while ($sync != chr(255) && $no_frame == false) {		# MPEG
				if (feof($fp)) $no_frame = true;
				else $sync = fread($fp, 1);
			}

			if ($no_frame == false) {
				fseek($fp, ftell($fp) - 1);
				$this->_frame_offset = ftell($fp) - 1;
				$this->_frame_header = fread($fp, 4);
				$this->_frame_found = true;
			} else $this->_frame_found = false;

			if (filesize($this->_filename) > 128) {					# ID3v1
				fseek($fp, filesize($this->_filename) - 128);
				$this->_id3v1 = fread($fp, 128);
			} else $this->_id3v1_included = false;

			fclose($fp);


			$this->_id3v1_included = (bool) (substr($this->_id3v1, 0, 3) == 'TAG');

			if ($this->_id3v2_included == true) {
				$this->_id3v2_major = ord($this->_id3v2_header[3]);
				$this->_id3v2_minor = ord($this->_id3v2_header[4]);

				if ($this->_id3v2_major > 4) {
					$this->_error = 'ID3v2.'.$this->_id3v2_major.' is not supported.'; return false;
				}
			}
		}

		return true;
	}

	############################################################################
	#									ID3								       #
	############################################################################

	############################################################################
	# Inhalt eines 'Text Information Frame' interpretieren
	# STRING _parse_text_frame(STRING frame_content)
	# frame_content		Inhalt des Frames
	function _parse_text_frame($frame_content) {
		#  Muster für FrameID 'T...' (nicht TXXX)
		#
		#  +-------------------------+
		#  |   (Frame Header T...)   |
		#  +-------------------------+
		#  | Text-Codierung (1 Byte) |
		#  +-------------------------+
		#  |	 	 Inhalt			 |
		#  +-------------------------+
		#
		#  Text-Codierungen:
		#  $00	ISO-8859-1
		#  $01	16-Bit Unicode (UTF-16) mit BOM*
		#  $02	16-Bit Unicode (UTF-16) ohne BOM*
		#  $03	8-Bit Unicode (UTF-8)*
		#
		#  *) werden noch nicht unterstützt
		#
		#########################################

		if (ord(substr($frame_content, 0, 1)) == 0) {		# ISO-8859-1
			$content = substr($frame_content, 1, strlen($frame_content) - 1);
		}
		elseif (ord(substr($frame_content, 0, 1)) == 1) {	# UTF-16 mit BOM
			$this->_error = 'UTF-16 text encoding in frame '.$frame_id.' is not yet supported.';
			return '';
		}
		elseif (ord(substr($frame_content, 0, 1)) == 2) {	# UTF-16 ohne BOM
			$this->_error = 'UTF-16 text encoding in frame '.$frame_id.' is not yet supported.';
			return '';
		}
		elseif (ord(substr($frame_content, 0, 1)) == 3) {	# UTF-8
			$this->_error = 'UTF-8 text encoding in frame '.$frame_id.' is not yet supported.';
			return '';
		}
		else {
			$this->_error = 'Unknown text encoding id '.ord(substr($frame_content, 0, 1)).' in frame '.$frame_id.'.';
			return '';
		}

		return $content;
	}

	############################################################################
	# Prüfen, ob sich irgendein Tag in der Datei befindet
	# BOOL tag_included()
	function tag_included() {
		$this->_read_id3();

		if ($this->_id3v1_included or $this->_id3v2_included) return true;
		else return false;
	}

	############################################################################
	# ID3 - Künstler
	# STRING artist()
	function artist() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TPE1'));
		}
		elseif ($this->_id3v1_included) return chop(substr($this->_id3v1, 33, 30));
		else return '';
	}

	############################################################################
	# ID3 - Titel
	# STRING title()
	function title() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TIT2'));
		}
		elseif ($this->_id3v1_included) return chop(substr($this->_id3v1, 3, 30));
		else return '';
	}

	############################################################################
	# ID3 - Album
	# STRING album()
	function album() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TALB'));
		}
		elseif ($this->_id3v1_included) return chop(substr($this->_id3v1, 63, 30));
		else return '';
	}

	############################################################################
	# ID3 - Genre
	# STRING genre()
	function genre() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TCON'));
		}
		elseif ($this->_id3v1_included) return _get_genre_v1($this->_genre_v1_id());
		else return '';
	}

	############################################################################
	# ID3 - Jahr
	# STRING year()
	function year() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TYER'));
		}
		elseif ($this->_id3v1_included) {
			$year = substr($this->_id3v1, 93, 4);
			if ($year > 0) return $year;
			else return '';
		}
		else return '';
	}

	############################################################################
	# ID3 - Track
	# INT track()
	function track() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return $this->_parse_text_frame($this->_id3v2_frame('TRCK'));
		}
		elseif ($this->_id3v1_included) {
			if (substr($this->_id3v1, 125, 1) == "\0" &&
				substr($this->_id3v1, 126, 1) != "\0") {	# Nur in ID3v1.1
				return ord(substr($this->_id3v1, 126, 1));
			}
		}
		else return 0;
	}

	############################################################################
	# ID3 - Kommentar
	# STRING comment()
	function comment() {
		#  Muster für FrameID 'COMM'
		#  +-------------------------+
		#  |   (Frame Header COMM)   |
		#  +-------------------------+
		#  | Text-Codierung (1 Byte) |
		#  +-------------------------+
		#  |    Sprache (3 Bytes)    |
		#  +-------------------------+
		#  |   Beschreibung + $00    |
		#  +-------------------------+
		#  |    Inhalt des Frames    |
		#  +-------------------------+
		#
		#  Text-Codierungen:
		#  $00	ISO-8859-1
		#  $01	16-Bit Unicode (UTF-16) mit BOM
		#  $02	16-Bit Unicode (UTF-16) ohne BOM
		#  $03	8-Bit Unicode (UTF-8)
		#
		##############################

		$this->_read_id3();

		if ($this->_id3v2_included) {
			$frame_content = $this->_id3v2_frame('COMM');
			$sep = strpos($frame_content, "\0", 4) + 1;

			return substr($frame_content, $sep, strlen($frame_content) - $sep);
		}
		elseif ($this->_id3v1_included) {
			if (substr($this->_id3v1, 125, 1) == "\0" &&
				substr($this->_id3v1, 126, 1) != "\0") {	# ID3v1.1
				return chop(substr($this->_id3v1, 97, 28));
			}
			else {											# ID3v1
				return chop(substr($this->_id3v1, 97, 30));
			}
		}
		else return '';
	}

	############################################################################
	# Genre-ID des ID3v1-Tags
	# INT genre_v1_id()
	function _genre_v1_id() {
		$this->_read_id3();

		return ord(substr($this->_id3v1,-1,1));
	}

	############################################################################
	# Inhalt eines ID3v2-Frames lesen
	# STRING _id3v2_frame(STRING frame_id)
	# frame_id		Eine ID3v2.3-FrameID (ID3v2.2-IDs werden übersetzt)
	function _id3v2_frame($frame_id) {
		$offset	= 0;
		$size = 0;
		$flags = '';
		$content = '';

		switch ($this->_id3v2_major) {
			case 2:
				# ID3v2.2 Frame ######################
				#
				#  +---------------------------------+
				#  |        FrameID (3 Bytes)        |
				#  +---------------------------------+
				#  | Länge (3 Bytes, 24-Bit Integer) |
				#  +---------------------------------+
				#  |             Inhalt              |
				#  +---------------------------------+
				#
				######################################

				$frame_id = $GLOBALS[v3_v2][$frame_id];

				$offset = strpos($this->_id3v2, $frame_id);
				if ($offset === false) return '';

				$size = big_endian_to_int(substr($this->_id3v2, $offset + 3, 3));

				$frame_content = substr($this->_id3v2, $offset + 6, $size);

				break;
			case 3:
				# ID3v2.3 Frame ######################
				#
				#  +---------------------------------+
				#  |        FrameID (4 Bytes)        |
				#  +---------------------------------+
				#  | Länge (4 Bytes, 32-Bit Integer) |
				#  +---------------------------------+
				#  |         Flags (2 Bytes)         |
				#  +---------------------------------+
				#  |             Inhalt              |
				#  +---------------------------------+
				#
				#  Flags:
				#  %abc00000 %ijk00000
				#  a - tag alter preservation
				#  b - file alter preservation
				#  c - read only
				#  i - compression (zlib)
				#  j - encryption
				#  k - grouping identity
				#
				######################################

				$offset = strpos($this->_id3v2, $frame_id);
				if ($offset === false) return '';

				$size = big_endian_to_int(substr($this->_id3v2, $offset + 4, 4));

				$second_flags = str_pad(decbin(ord(substr($this->_id3v2, $offset + 9, 1))) , 8, '0', STR_PAD_LEFT);

				if ($second_flags[1] == true) {		# Verschlüsselung
					$method_id = ord(substr($this->_id3v2, $offset + 10, 1));
					$encrypted = substr($this->_id3v2, $offset + 11, $size - 1);

					$frame_content = $encrypted;

					break;
				}

				if ($second_flags[0] == true) {		# GZip-Kompression
					$length_uncompressed = big_endian_to_int(substr($this->_id3v2, $offset + 10, 4));
					$compressed = substr($this->_id3v2, $offset + 14, $size - 4);

					$frame_content = gzuncompress($compressed, $length_uncompressed);
					if ($frame_content == false) return false;

					break;
				}

				$frame_content = substr($this->_id3v2, $offset + 10, $size);

				break;
			case 4:
				# ID3v2.4 Frame ################################
				#
				#  +-------------------------------------------+
				#  |              FrameID (4 Bytes)            |
				#  +-------------------------------------------+
				#  | Länge (4 Bytes, 28-Bit SynchSafe Integer) |
				#  +-------------------------------------------+
				#  |               Flags (2 Bytes)             |
				#  +-------------------------------------------+
				#  |                   Inhalt                  |
				#  +-------------------------------------------+
				#
				#  Flags:
				#  %0abc0000 %0h00kmnp
				#  a - tag alter preservation
				#  b - file alter preservation
				#  c - read only
				#  h - grouping identity
				#  k - compression (zlib deflate)
				#  m - encryption
				#  n - unsynchronisation
				#  p - data length indicator
				#
				################################################

				$offset = strpos($this->_id3v2, $frame_id);
				if ($offset === false) return '';

				$size = big_endian_to_int(substr($this->_id3v2, $offset + 4, 4), true);

				$frame_content = substr($this->_id3v2, $offset + 10, $size);

				break;
		}

		return $frame_content;
	}

	############################################################################
	# Version des ID3-Tags
	# STRING id3_version()
	function id3_version() {
		$this->_read_id3();

		if ($this->_id3v2_included) {
			return 'ID3v2.'.$this->_id3v2_major.'.'.$this->_id3v2_minor;
		}
		elseif ($this->_id3v1_included) {
			if (substr($this->_id3v1, 125, 1) == "\0" &&
				substr($this->_id3v1, 126, 1) != "\0") {
				return 'ID3v1.1';
			}
			else return 'ID3v1';
		}
		else {
			return false;
		}
	}

	function last_error() {
		return $this->_error;
	}


	############################################################################
	#							MPEG-Frame-Header							   #
	############################################################################


	############################################################################
	# Dauer des Songs (in Sekunden)
	# INT|STRING length(BOOL format)
	# format		Gibt an, ob die Ausgabe in mm:ss formatiert (true) oder in
	#				Sekunden (false) belassen werden soll
	function length($format = false) {
		$this->_read_id3();

		if ($this->bitrate() > 0) {
			$length = floor( ((8 * filesize($this->_filename))/1000) / $this->bitrate() );

			if ($format == true) {
				$length = sprintf('%01d:%02d',floor($length/60),floor($length-(floor($length/60)*60)));
			}
		}
		else {
			$length = -1;
		}

		return $length;
	}

	############################################################################
	# Prüfen, ob die Frames gepaddet sind
	# INT padding()
	# Rückgabe:		0 | 1 (nicht gepaddet | gepaddet)
	function padding() {
		$this->_read_id3();

		return ((ord(substr($this->_header,2,1)) % 4)/2);
	}

	############################################################################
	# Bitrate (in kBit/s)
	# INT bitrate()
	function bitrate() {
		$_layer_2 =			array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384);
		$_layer_3_mpeg_1 =	array(0,32,40,48,56,64,80, 96,112,128,160,192,224,256,320);
		$_layer_3_mpeg_2 =	array(0, 8,16,24,32,64,80, 56, 64,128,160,112,128,256,320);

		$this->_read_id3();

		$layer = ord(substr($this->_frame_header,1,1))/2 % 4;

		switch ($layer) {
			case 3:		# Layer 1
				$bitrate = (ord(substr($this->_frame_header,2,1))/16) * 32;
				break;
			case 2:		# Layer 2
				$birate = $_layer_2[ ord(substr($this->_frame_header,2,1))/16 ];
				break;
			case 1:		# Layer 3
				if ((ord(substr($this->_frame_header,1,1))/8 % 2) == 0 ) {	# MPEG 2.0
					$bitrate = $_layer_3_mpeg_2[ ord(substr($this->_frame_header,2,1))/16 ];
				}
				else {													# MPEG 1.0
					$bitrate = $_layer_3_mpeg_1[ ord(substr($this->_frame_header,2,1))/16 ];
				}
				break;
		}

		return $bitrate;
	}

	############################################################################
	# MPEG-Layer
	# STRING layer()
	function layer() {
		$_layers = array('','Layer III','Layer II','Layer I');

		$this->_read_id3();

		return $_layers[ ord(substr($this->_frame_header,1,1))/2 % 4 ];
	}

	############################################################################
	# MPEG-Version
	# STRING id()
	function id() {
		$_ids = array('MPEG 2.0', 'MPEG 1.0');

		$this->_read_id3();

		if ($this->_frame_found == false) return '';
		else return $_ids[ ord(substr($this->_frame_header,1,1))/8 % 2 ];
	}

	############################################################################
	# Sample-Rate (in Hz)
	# INT frequency()
	function frequency() {
		$_mpeg_1 = array(44100,48000,32000);
		$_mpeg_2 = array(22050,24000,16000);

		$this->_read_id3();

		if ( (ord(substr($this->_frame_header,1,1))/8 % 2) == 0 ) {		# MPEG 2.0
			return $_mpeg_2[ (ord(substr($this->_frame_header,2,1)) % 16)/4 ];
		}
		else {		# MPEG 1.0
			return $_mpeg_1[ (ord(substr($this->_frame_header,2,1)) % 16)/4 ];
		}
	}

	############################################################################
	# 'Privat'-Schalter
	# BOOL private()
//	function private() {
//		$this->_read_id3();
//
//		return (bool) (ord(substr($this->_frame_header,2,1)) % 2);
//	}

	############################################################################
	# 'Copyright'-Schalter
	# BOOL copyright()
	function copyright() {
		$this->_read_id3();

		return (bool) ((ord(substr($this->_frame_header,-1,1)) % 16)/8);
	}

	############################################################################
	# 'Original'-Schalter
	# BOOL original()
	function original() {
		$this->_read_id3();

		return (bool) (ord((substr($this->_frame_header,-1,1) % 8)/4));
	}

	############################################################################
	# Modus
	# STRING mode()
	function mode() {
		$_modes = array('Stereo','Joint Stereo','Dual Channel','Mono');

		$this->_read_id3();

		return $_modes[ ord(substr($this->_frame_header,-1,1))/64 ];
	}

	############################################################################
	# Methode zur Rauschunterdrückung
	# STRING emphasis()
	function emphasis() {
		$_emphasis = array('-','50/15ms','','CCITT j.17');

		$this->_read_id3();

		return $_emphasis[ ord(substr($this->_frame_header,-1,1)) % 4 ];
	}

	############################################################################
	# Frame-Größe
	# INT framesize()
	function framesize() {
		$this->_read_id3();

		return floor((144 * $this->bitrate() * 1000 / $this->frequency()) + $this->padding());
	}

	############################################################################
	# Prüfen, ob die Frames CRCs enthalten
	# BOOL protection()
	function protection() {
		$this->_read_id3();

		if ( (ord(substr($this->_frame_header,1,1)) % 2) == 0 ) return true;
		else return false;
	}

	############################################################################
	# Offset des ersten MPEG-Frames
	# INT frame_offset()
	function frame_offset() {
		$this->_read_id3();

		return $this->_frame_offset;
	}

}


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

	return $_genres[$number];
}


################################################################################
# Big-Endian-Byte-Wort in Integerwert konvertieren
# INT big_endian_to_int(STRING byteword, BOOL synchsafe, BOOL signed)
# byteword		Das zu behandelnde Byte-Wort
# synchsafe		Gibt an, ob das jeweilige MSB vernachlässigt werden soll
# signed		Gibt an, ob der Integerwert ein Vorzeichen enthalten soll
function big_endian_to_int($byteword, $synchsafe = false, $signed = false) {
	$intvalue = 0;
	$bytewordlen = strlen($byteword);

	for ($i = 0; $i < $bytewordlen; $i++) {
		if ($synchsafe) {	# MSB (most significant bit) vernachlässigen, effektiv 7-Bit Bytes
			$intvalue = $intvalue | (ord($byteword[$i]) & 0x7F) << (($bytewordlen - 1 - $i) * 7);
		} else {
			$intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i));
		}
	}

	if ($signed && !$synchsafe) {	# Synchsafe Integer dürfen kein Vorzeichen haben
		if ($bytewordlen == 4) {
			$signmaskbit = 0x80 << (8 * ($bytewordlen - 1));
			if ($intvalue & $signmaskbit) {
				$intvalue = 0 - ($intvalue & ($signmaskbit - 1));
			}
		}
	}
	return intval(floor($intvalue));
}

?>