Location: PHPKode > scripts > MP3 Class > mp3-class/class.mp3.php
<?php

/*
	$Author: mfboy
	$Version: 0.0.1
	$Date: 2009-03-29
*/

/*

	(array|false) mp3::get_mp3($filename[, $getframesindex = false])
	(true|false)  mp3::set_mp3($file_input, $file_output[, $id3v1 = array(), $id3v2 = array()])
	(true|false)  mp3::cut_mp3($file_input, $file_output[, $startframeindex = 0, $endframeindex = -1, $framesindex = array()])

	(array|false) mp3::get_frameheader($frameheaders)
	(string)      mp3::conv_flag($flag)
	(string)      mp3::conv_time($seconds)

*/

class mp3 {

	private $fp, $filesize;
	private $id3v1, $id3v2, $data;

	private $data_start = 0;
	private $data_end = 0;
	private $data_frames = array();

	private $vbr_bitrate_max = 0;
	private $vbr_bitrate_min = 0;
	private $vbr_bitrates = 0;
	private $vbr_frametotal = 0;

	var $id3v1_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' => 'AlternRock',
		'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 American',
		'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' => 'Advantgarde',
		'91' => 'Gothic Rock',
		'92' => 'Progressive Rock',
		'93' => 'Psychadelic 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',
		);

	var $id3v2_frameid_descriptions = array
		(
		'AENC' => 'Audio encryption',
		'APIC' => 'Attached picture',
		'COMM' => 'Comments',
		'COMR' => 'Commercial frame',
		'ENCR' => 'Encryption method registration',
		'EQUA' => 'Equalization',
		'ETCO' => 'Event timing codes',
		'GEOB' => 'General encapsulated object',
		'GRID' => 'Group identification registration',
		'IPLS' => 'Involved people list',
		'LINK' => 'Linked information',
		'MCDI' => 'Music CD identifier',
		'MLLT' => 'MPEG location lookup table',
		'OWNE' => 'Ownership frame',
		'PRIV' => 'Private frame',
		'PCNT' => 'Play counter',
		'POPM' => 'Popularimeter',
		'POSS' => 'Position synchronisation frame',
		'RBUF' => 'Recommended buffer size',
		'RVAD' => 'Relative volume adjustment',
		'RVRB' => 'Reverb',
		'SYLT' => 'Synchronized lyric/text',
		'SYTC' => 'Synchronized tempo codes',
		'TALB' => 'Album/Movie/Show title',
		'TBPM' => 'BPM (beats per minute)',
		'TCOM' => 'Composer',
		'TCON' => 'Content type',
		'TCOP' => 'Copyright message',
		'TDAT' => 'Date',
		'TDLY' => 'Playlist delay',
		'TENC' => 'Encoded by',
		'TEXT' => 'Lyricist/Text writer',
		'TFLT' => 'File type',
		'TIME' => 'Time',
		'TIT1' => 'Content group description',
		'TIT2' => 'Title/songname/content description',
		'TIT3' => 'Subtitle/Description refinement',
		'TKEY' => 'Initial key',
		'TLAN' => 'Language(s)',
		'TLEN' => 'Length',
		'TMED' => 'Media type',
		'TOAL' => 'Original album/movie/show title',
		'TOFN' => 'Original filename',
		'TOLY' => 'Original lyricist(s)/text writer(s)',
		'TOPE' => 'Original artist(s)/performer(s)',
		'TORY' => 'Original release year',
		'TOWN' => 'File owner/licensee',
		'TPE1' => 'Lead performer(s)/Soloist(s)',
		'TPE2' => 'Band/orchestra/accompaniment',
		'TPE3' => 'Conductor/performer refinement',
		'TPE4' => 'Interpreted, remixed, or otherwise modified by',
		'TPOS' => 'Part of a set',
		'TPUB' => 'Publisher',
		'TRCK' => 'Track number/Position in set',
		'TRDA' => 'Recording dates',
		'TRSN' => 'Internet radio station name',
		'TRSO' => 'Internet radio station owner',
		'TSIZ' => 'Size',
		'TSRC' => 'ISRC (international standard recording code)',
		'TSSE' => 'Software/Hardware and settings used for encoding',
		'TYER' => 'Year',
		'UFID' => 'Unique file identifier',
		'USER' => 'Terms of use',
		'USLT' => 'Unsychronized lyric/text transcription',
		'WCOM' => 'Commercial information',
		'WCOP' => 'Copyright/Legal information',
		'WOAF' => 'Official audio file webpage',
		'WOAR' => 'Official artist/performer webpage',
		'WOAS' => 'Official audio source webpage',
		'WORS' => 'Official internet radio station homepage',
		'WPAY' => 'Payment',
		'WPUB' => 'Publishers official webpage'
		);

	var $bitrates = array
		(
		'0000' => array
			(
			1 => array('~', '~', '~'),
			2 => array('~', '~', '~')
			),
		'0001' => array
			(
			1 => array('32', '32', '32'),
			2 => array('32', '8', '8')
			),
		'0010' => array
			(
			1 => array('64', '48', '40'),
			2 => array('48', '16', '16')
			),
		'0011' => array
			(
			1 => array('96', '56', '48'),
			2 => array('56', '24', '24')
			),
		'0100' => array
			(
			1 => array('128', '64', '56'),
			2 => array('64', '32', '32')
			),
		'0101' => array
			(
			1 => array('160', '80', '64'),
			2 => array('80', '40', '40')
			),
		'0110' => array
			(
			1 => array('192', '96', '80'),
			2 => array('96', '48', '48')
			),
		'0111' => array
			(
			1 => array('224', '112', '96'),
			2 => array('112', '56', '56')
			),
		'1000' => array
			(
			1 => array('256', '128', '112'),
			2 => array('128', '64', '64')
			),
		'1001' => array
			(
			1 => array('288', '160', '128'),
			2 => array('144', '80', '80')
			),
		'1010' => array
			(
			1 => array('320', '192', '160'),
			2 => array('160', '96', '96')
			),
		'1011' => array
			(
			1 => array('352', '224', '192'),
			2 => array('176', '112', '112')
			),
		'1100' => array
			(
			1 => array('384', '256', '224'),
			2 => array('192', '128', '128')
			),
		'1101' => array
			(
			1 => array('416', '320', '256'),
			2 => array('224', '144', '144')
			),
		'1110' => array
			(
			1 => array('448', '384', '320'),
			2 => array('256', '160', '160')
			)
		);

	var $sampling_frequencys = array
		(
		'00' => array('44100', '22050', '11025'),
		'01' => array('48000', '24000', '12000'),
		'10' => array('32000', '16000', '8000')
		);

	var $modes = array
		(
		'00' => 'Stereo',
		'01' => 'Joint Stereo',
		'10' => 'Dual Channel',
		'11' => 'Single Channel'
		);

	var $mode_extensions = array
		(
		'00' => array(0, 0),
		'01' => array(1, 0),
		'10' => array(0, 1),
		'11' => array(1, 1),
		);

	function get_mp3($filename, $getframesindex = false) {

		if(!$this->fp = @fopen($filename, 'rb')) {
			return false;
		}

		$this->filesize = filesize($filename);
		$this->data = array();

		$this->data_start = 0;
		$this->data_frames = array();

		$this->vbr_bitrate_min = $this->vbr_bitrate_max = 0;
		$this->vbr_bitrates = $this->vbr_frametotal = 0;

		$this->id3v2 = $this->get_id3v2();
		$this->id3v1 = $this->get_id3v1();

		$this->data = $this->get_data();
		$return = array();

		if($this->data) {
			$return['Data'] = $this->data;
		}

		if($this->id3v2) {
			$return['ID3v2'] = $this->id3v2;
		}

		if($this->id3v1) {
			$return['ID3v1'] = $this->id3v1;
		}

		if($getframesindex && $return['Data']) {

			$return['Frames'] = $this->get_framesindex($return['Data']['Type'], $return['Data']['Frametotal']);
			$return['Data']['Frametotal'] = count($return['Frames']);

			$return['Data']['Length'] = ceil($return['Data']['Frametotal'] * 0.026);
			$return['Data']['Time'] = $this->conv_time($return['Data']['Length']);

			if($this->vbr_bitrates) {
				$return['Data']['Bitrate'] = round($this->vbr_bitrates / $this->vbr_frametotal);
				$return['Data']['Bitrate_min'] = $this->vbr_bitrate_min;
				$return['Data']['Bitrate_max'] = $this->vbr_bitrate_max;
			}

		}

		fclose($this->fp);

		return $return;

	}

	private function get_id3v2() {

		rewind($this->fp);
		$tag = array();

		$headerformat = 'a3Header/CVersion/CRevision/CFlag/CSize0/CSize1/CSize2/CSize3';
		$headersize = 10;

		$headerdata = fread($this->fp, $headersize);
		$header = @unpack($headerformat, $headerdata);

		if(!$header || $header['Header'] != 'ID3') {
			rewind($this->fp);
			return false;
		}

		$tag['Version'] = $header['Version'];
		$tag['Revision'] = $header['Revision'];

		$tagflag = $this->conv_flag($header['Flag']);

		$tag['Flag'] = array
			(
			'Unsynchronisation' => $tagflag{0},
			'Extra' => $tagflag{1},
			'Test' => $tagflag{2}
			);

		$tagsize = ($header['Size0'] & 0x7F) * 0x200000
			+ ($header['Size1'] & 0x7F) * 0x400
			+ ($header['Size2'] & 0x7F) * 0x80
			+ ($header['Size3']);

		if(($tagsize = intval($tagsize)) < 1) {
			return $tag;
		}

		$tag['Size'] = $tagsize;
		$tag['Frames'] = array();

		$seek_start = ftell($this->fp);
		$seek_end = $seek_start + $tagsize - $headersize;

		while(1) {

			$seek_now = ftell($this->fp);
			$seek_last = $seek_end - $seek_now;

			if($seek_last <= 0) {
				break;
			}

			$frameheaderformat = 'a4FrameID/CSize0/CSize1/CSize2/CSize3/CFlag0/CFlag1/cCharset';
			$frameheader = @unpack($frameheaderformat, fread($this->fp, 11));

			if(!$frameheader || !$frameheader['FrameID'] || !$frameheader['Size3']) {
				continue;
			}

			$frameid = $frameheader['FrameID'];
			$framedescription = 'Unknown';

			if(isset($this->id3v2_frameid_descriptions[$frameid])) {
				$framedescription = $this->id3v2_frameid_descriptions[$frameid];
			} else {
				switch($frameid{0}) {
					case 'T': $framedescription = 'User defined text information frame'; break;
					case 'W': $framedescription = 'User defined URL link frame'; break;
				}
			}

			$framesize = $frameheader['Size0'] * 0x100000000
					+ $frameheader['Size1'] * 0x10000
					+ $frameheader['Size2'] * 0x100
					+ $frameheader['Size3'];

			if($framesize < 1 || $framesize > $seek_last) {
				continue;
			}

			$frameflag = array
				(
				$this->conv_flag($frameheader['Flag0']),
				$this->conv_flag($frameheader['Flag1'])
				);

			switch($frameheader['Charset']) {
				case 0: $framecharset = 'ISO-8859-1'; break;
				case 1: $framecharset = 'UTF-16'; break;
				case 2: $framecharset = 'UTF-16BE'; break;
				case 3: $framecharset = 'UTF-8'; break;
				default: $framecharset = 'Unknown'; break;
			}

			$framedatasize = $framesize - 1;
			$framedata = @unpack("a{$framedatasize}Data", fread($this->fp, $framedatasize));

			if($frameid == 'COMM') {
				$framelang = substr($framedata['Data'], 0, 3);
				$framedata = substr($framedata['Data'], 3 + ($framedata['Data']{3} == "\x00" ? 1 : 0));
			} else {
				$framelang = false;
				$framedata = $framedata['Data'];
			}

			$frame = array
				(
				'FrameID' => $frameid,
				'Description' => $framedescription,
				'Flag' => array
					(
					'Tag_protect' => $frameflag[0]{0},
					'File_protect' => $frameflag[0]{1},
					'Readonly' => $frameflag[0]{2},
					'Compressed' => $frameflag[1]{0},
					'Encrypt' => $frameflag[1]{1},
					'Group' => $frameflag[1]{2},
					),
				'Size' => $framesize,
				'Charset' => $framecharset,
				'Data' => $framedata
				);

			if($framelang) {
				$frame['Language'] = $framelang;
			}

			$tag['Frames'][$frameid][] = $frame;

		}

		$this->data_start = $seek_end;
		rewind($this->fp);

		return $tag;

	}

	private function get_id3v1() {

		$tagsize = 128;
		$tagstart = $this->filesize - $tagsize;

		fseek($this->fp, $tagstart);

		$tagformat = 'a3Header/a30Title/a30Artist/a30Album/a4Year/a28Comment/CReserve/CTrack/CGenre';
		$tag = @unpack($tagformat, fread($this->fp, $tagsize));

		if($tag['Header'] == 'TAG') {
			$this->data_end = $this->filesize - $tagsize;
		} else {
			$this->data_end = $this->filesize;
			return false;
		}

		$tag['Genre'] = $this->id3v1_genres[$tag['Genre']];
		$tag['Genre'] = $tag['Genre'] ? $tag['Genre'] : 'Unknown';

		unset($tag['Header']);
		rewind($this->fp);

		return $tag;

	}

	private function get_data() {

		fseek($this->fp, $this->data_start);

		$blank_data = fread($this->fp, 1024);
		$blank_size = @max(0, strpos($blank_data, trim($blank_data)));

		fseek($this->fp, $this->data_start + $blank_size);

		$frameheaders = fread($this->fp, 4);
		$frameheader = $this->get_frameheader($frameheaders);

		if(!$frameheader || !is_array($frameheader)) {
			return false;
		}

		extract($frameheader);

		$framedata = fread($this->fp, 36);
		$frametype = strpos($framedata, 'Xing') ? 'VBR' : 'CBR';

		if($frametype == 'CBR') {
			$frametotal = $this->get_data_cbr($frameheader, $mpegver, $layer, $bitrate, $sampling_frequency, $padding, $quotiety);
		} else {
			$frametotal = $this->get_data_vbr($frameheader);
		}

		$framelength = ceil($frametotal * 0.026);
		$frametime = $this->conv_time($framelength);

		$return = array
			(
			'Filesize' => $this->filesize,
			'MPEGVer' => $mpegver,
			'Layer' => $layer,
			'Bitrate' => $bitrate,
			'Sampling_frequency' => $sampling_frequency,
			'Mode' => $mode,
			'Mode_extension' => array
				(
				'Intensity_Stereo' => $mode_extension[0],
				'MS_Stereo' => $mode_extension[1]
				),
			'Copyright' => $copyright,
			'Original' => $original,
			'Type' => $frametype,
			'Length' => $framelength,
			'Time' => $frametime,
			'Frametotal' => $frametotal
			);

		return $return;

	}

	private function get_data_cbr($frameheader, $mpegver, $layer, $bitrate, $sampling_frequency, $padding, $quotiety) {

		$framedatasize = $this->data_end - $this->data_start;
		$framesize = intval($quotiety * $bitrate * 1000 / $sampling_frequency + intval($padding));

		return @ceil($framedatasize / $framesize);

	}

	private function get_data_vbr($frameheader) {

		$framevbrdata = @unpack('NVBR', fread($this->fp, 4));;
		$framevbrs = array(1, 3, 5, 7, 9, 11, 13, 15);

		if(!in_array($framevbrdata['VBR'], $framevbrs)) {
			return 0;
		}

		$frametotaldata = @unpack('NFrametotal', fread($this->fp, 4));
		$frametotal = $frametotaldata['Frametotal'];

		return $frametotal;

	}

	function get_frameheader($frameheaders) {

		$frameheader = array();

		for($i = 0; $i < 4; $i++) {
			$frameheader[] = $this->conv_flag(ord($frameheaders{$i}));
		}

		if($frameheaders{0} != "\xFF" || substr($frameheader[1], 0, 3) != '111') {
			return false;
		}

		switch(substr($frameheader[1], 3, 2)) {
			case '00': $mpegver = '2.5'; break;
			case '10': $mpegver = '2'; break;
			case '11': $mpegver = '1'; break;
			default: return false;
		}

		switch(substr($frameheader[1], 5, 2)) {
			case '01': $layer = '3'; break;
			case '10': $layer = '2'; break;
			case '11': $layer = '1'; break;
			default: return false;
		}

		$bitrate = substr($frameheader[2], 0, 4);
		$bitrate = $this->bitrates[$bitrate][intval($mpegver)][intval($layer) - 1];

		$sampling_frequency = substr($frameheader[2], 5, 2);
		$sampling_frequency = $this->sampling_frequencys[$sampling_frequency][ceil($mpegver) - 1];

		if(!$bitrate || !$sampling_frequency) {
			return false;
		}

		$padding = $frameheader[2]{6};

		$mode = substr($frameheader[3], 0, 2);
		$mode = $this->modes[$mode];

		$mode_extension = substr($frameheader[3], 2, 2);
		$mode_extension = $this->mode_extensions[$mode_extension];

		if(!$mode || !$mode_extension) {
			return false;
		}

		$copyright = substr($frameheader[3], 4, 1) ? 1 : 0;
		$original = substr($frameheader[3], 5, 1) ? 1 : 0;

		switch($mpegver) {
			case '1':
				$quotiety = $layer == '1' ? 48 : 144;
				break;
			case '2': case '2.5':
				$quotiety = $layer == '1' ? 24 : 72;
				break;
			default:
				return false;
		}

		return array
			(
			'mpegver' => $mpegver,
			'layer' => $layer,
			'bitrate' => $bitrate,
			'sampling_frequency' => $sampling_frequency,
			'padding' => $padding,
			'mode' => $mode,
			'mode_extension' => $mode_extension,
			'copyright' => $copyright,
			'original' => $original,
			'quotiety' => $quotiety
			);

	}

	private function get_framesindex($frametype, $frametotal) {

		$framessize = $this->data_end - $this->data_start;
		$frameindexs = array();

		fseek($this->fp, $this->data_start);

		if($frametype == 'CBR') {

			$framesize = intval($framessize / $frametotal);
			$framedatasize = $framesize - 4;

			for($i = 0; $i < $frametotal; ) {

				$frameheaders = fread($this->fp, 4);
				$frameseek = ftell($this->fp);
				$frameheader = array();

				if(($this->data_end - $frameseek) < $framesize) {
					break;
				}

				for($j = 0; $j < 4; $j++) {
					$frameheader[] = $this->conv_flag(ord($frameheaders{$j}));
				}

				if($frameheaders{0} == "\xFF" && substr($frameheader[1], 0, 3) == '111') {

					$frameindexs[] = $frameseek - 4;
					$i++;

					fseek($this->fp, $frameseek + $framedatasize);
					continue;

				}

				fseek($this->fp, $frameseek - 3);

			}

		} elseif ($frametype == 'VBR') {

			$blank_data = fread($this->fp, 1024);
			$blank_size = @max(0, strpos($blank_data, trim($blank_data)));

			fseek($this->fp, $this->data_start + $blank_size);

			for($i = 0; $i < $frametotal; $i++) {

				$frameheaders = fread($this->fp, 4);
				$frameseek = ftell($this->fp);

				if($frameseek >= $this->data_end) {
					break;
				}

				if(!$framedata = $this->get_frameheader($frameheaders)) {
					continue;
				}

				extract($framedata);

				$this->vbr_bitrate_min = $this->vbr_bitrate_min > 0 ? min($this->vbr_bitrate_min, $bitrate) : $bitrate;
				$this->vbr_bitrate_max = max($this->vbr_bitrate_max, $bitrate);

				$this->vbr_bitrates += $bitrate;
				$this->vbr_frametotal++;

				$framesize = intval($quotiety * $bitrate * 1000 / $sampling_frequency + intval($padding));
				$frameindexs[] = $frameseek - 4;

				fseek($this->fp, $frameseek + $framesize - 4);

			}

		}

		return $frameindexs;

	}

	function set_mp3($file_input, $file_output, $id3v1 = array(), $id3v2 = array()) {

		if(!$this->fp = @fopen($file_input, 'rb')) {
			return false;
		}

		if(!$fp = @fopen($file_output, 'wb')) {
			return false;
		}

		$this->filesize = filesize($file_input);
		$this->id3v2 = $this->get_id3v2();
		$this->id3v1 = $this->get_id3v1();

		fseek($this->fp, $this->data_start);

		$mp3_data_size = $this->data_end - $this->data_start;
		$mp3_data = @fread($this->fp, $mp3_data_size);

		$id3v1 = is_array($id3v1) ? $id3v1 : array();
		$id3v2 = is_array($id3v2) ? $id3v2 : array();

		$id3v2['frames'] = is_array($id3v2['frames']) ? $id3v2['frames'] : array();
		$id3v2_data = '';

		foreach($id3v2['frames'] as $frame) {

			if(!is_array($frame) || !$frame) {
				continue;
			}

			if(strlen($frame['frameid']) != 4) {
				continue;
			}

			$frame['tagprotect'] = !empty($frame['tagprotect']) ? 1 : 0;
			$frame['fileprotect'] = !empty($frame['fileprotect']) ? 1 : 0;
			$frame['readonly'] = !empty($frame['readonly']) ? 1 : 0;
			$frame['compressed'] = !empty($frame['compressed']) ? 1 : 0;
			$frame['encrypt'] = !empty($frame['encrypt']) ? 1 : 0;
			$frame['group'] = !empty($frame['group']) ? 1 : 0;

			$frame['Flag0'] = bindec("{$frame[tagprotect]}{$frame[fileprotect]}{$frame[readonly]}00000");
			$frame['Flag1'] = bindec("{$frame[compressed]}{$frame[encrypt]}{$frame[group]}00000");

			$frame['language'] = in_array($frame['language'], array(0, 1, 2, 3)) ? $frame['language'] : 0;
			$frame['data'] = $frame['data'] ? $frame['data'] : '';

			$id3v2_data .= pack('a4NCCc', $frame['frameid'], strlen($frame['data']) + 1, $frame['Flag0'], $frame['Flag1'], $frame['language']);
			$id3v2_data .= $frame['data'];

		}

		if($id3v2_data) {

			$id3v2['unsynchronisation'] = !empty($id3v2['unsynchronisation']) ? 1 : 0;
			$id3v2['extra'] = !empty($id3v2['extra']) ? 1 : 0;
			$id3v2['test'] = !empty($id3v2['test']) ? 1 : 0;

			$id3v2['Flag0'] = bindec("{$id3v2[unsynchronisation]}{$id3v2[extra]}{$id3v2[test]}00000");

			$id3v2_size = strlen($id3v2_data) + 10;
			$id3v2_sizes = array();

			for($i = 3; $i >= 0; $i--) {
				$id3v2_sizes[$i] = ($id3v2_size & 0x7F);
				$id3v2_size /= 80;
			}

			fwrite($fp, pack('a3CCCCCCC', 'ID3', 3, 0, $id3v2['Flag0'], $id3v2_sizes[0], $id3v2_sizes[1], $id3v2_sizes[2], $id3v2_sizes[3]));
			fwrite($fp, $id3v2_data);

		}

		fwrite($fp, $mp3_data);

		if($id3v1 && is_array($id3v1)) {
			$id3v1_data = pack('a3a30a30a30a4a28CCC', 'TAG', $id3v1[0], $id3v1[1], $id3v1[2], $id3v1[3], $id3v1[4], $id3v1[5], $id3v1[6], $id3v1[7]);
			fwrite($fp, $id3v1_data);
		}

		fclose($this->fp);
		fclose($fp);

		return true;

	}

	function cut_mp3($file_input, $file_output, $startframeindex = 0, $endframeindex = -1, $framesindex = array()) {

		if(!$framesindex) {
			$mp3_info = $this->get_mp3($file_input, true);
			if(!$mp3_info || !is_array($mp3_info) || !$mp3_info['Frames']) {
				return false;
			}
		}

		if(!$this->fp = @fopen($file_input, 'rb')) {
			return false;
		}

		if(!$fp = @fopen($file_output, 'wb')) {
			return false;
		}

		$framesindex = $mp3_info['Frames'];
		$frametotal = count($framesindex);

		$framelast = $frametotal - 1;
		$framelastsize = $this->data_end - $framesindex[$framelast];

		$startframeindex = max(0, $startframeindex);
		$endframeindex = $endframeindex < 0 ? $framelast : $endframeindex;

		if($startframeindex >= $endframeindex || $endframeindex > $framelast) {
			return false;
		}

		for($i = $startframeindex; $i <= $endframeindex; $i++) {
			fseek($this->fp, $framesindex[$i]);
			fwrite($fp, fread($this->fp, $i == $framelast ? $framelastsize : ($framesindex[$i + 1] - $framesindex[$i])));
		}

		fclose($this->fp);
		fclose($fp);

		return true;

	}

	function conv_flag($flag) {

		$flag = decbin($flag);
		$scarcity = 8 - strlen($flag);

		if($scarcity < 1) {
			return $flag;
		}

		for($i = 0; $i < $scarcity; $i++) {
			$flag = '0'.$flag;
		}

		return $flag;

	}

	function conv_time($seconds) {

		$return = '';
		$separator = ':';

		if($seconds > 3600) {
			$return .= intval($seconds / 3600).' ';
			$seconds -= intval($seconds / 3600) * 3600;
		}

		if($seconds > 60) {
			$return .= sprintf('%02d', intval($seconds / 60)).' ';
			$seconds -= intval($seconds / 60) * 60;
		} else {
			$return .= '00 ';
		}

		$return .= sprintf('%02d', $seconds);
		$return = trim($return);

		return str_replace(' ', $separator, $return);

	}

}

?>
Return current item: MP3 Class