Просмотр файла zagrcent/id.php

Размер файла: 31.18Kb
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This code is released under the GNU LGPL Go read it over here: |
  9. // | http://www.gnu.org/copyleft/lesser.html |
  10. // +----------------------------------------------------------------------+
  11. // | Authors: Sandy McArthur Jr. <Leknor@Leknor.com> |
  12. // +----------------------------------------------------------------------+
  13. //
  14. // $Id: Id.php,v 1.11 2005/10/04 10:58:20 alexmerz Exp $
  15. //
  16.  
  17. // Uncomment the folling define if you want the class to automatically
  18. // read the MPEG frame info to get bitrate, mpeg version, layer, etc.
  19. //
  20. // NOTE: This is needed to maintain pre-version 1.0 behavior which maybe
  21. // needed if you are using info that is from the mpeg frame. This includes
  22. // the length of the song.
  23. //
  24. // This is discouraged because it will siginfincantly lengthen script
  25. // execution time if all you need is the ID3 tag info.
  26. // define('ID3_AUTO_STUDY', true);
  27.  
  28. // Uncomment the following define if you want tons of debgging info.
  29. // Tip: make sure you use a <PRE> block so the print_r's are readable.
  30. // define('ID3_SHOW_DEBUG', true);
  31.  
  32. require_once "pear.php" ;
  33.  
  34. /**
  35. * File not opened
  36. * @const PEAR_MP3_ID_FNO
  37. */
  38. define('PEAR_MP3_ID_FNO', 1);
  39.  
  40. /**
  41. * Read error
  42. * @const PEAR_MP3_ID_RE
  43. */
  44. define('PEAR_MP3_ID_RE', 2);
  45.  
  46. /**
  47. * Tag not found
  48. * @const PEAR_MP3_ID_TNF
  49. */
  50. define('PEAR_MP3_ID_TNF', 3);
  51.  
  52. /**
  53. * File is not a MP3 file (corrupted?)
  54. * @const PEAR_MP3_ID_NOMP3
  55. */
  56. define('PEAR_MP3_ID_NOMP3', 4);
  57.  
  58. /**
  59. * A Class for reading/writing MP3 ID3 tags
  60. *
  61. * Note: This code doesn't try to deal with corrupt mp3s. So if you get
  62. * incorrect length times or something else it may be your mp3. To fix just
  63. * re-enocde from the CD. :~)
  64. *
  65. * eg:
  66. * require_once("MP3/Id.php");
  67. * $file = "Some Song.mp3";
  68. *
  69. * $id3 = &new MP3_Id();
  70. * $id3->read($file);
  71. * print_r($id3);
  72. *
  73. * echo $id3->getTag('artists');
  74. *
  75. * $id3->comment = "Be gentle with that file.";
  76. * $id3->write();
  77. * $id3->read($file);
  78. * print_r($id3 );
  79. *
  80. * @package MP3_Id
  81. * @author Sandy McArthur Jr. <Leknor@Leknor.com>
  82. * @version $Version$
  83. */
  84. class MP3_Id {
  85.  
  86. /**
  87. * mp3/mpeg file name
  88. * @var boolean
  89. */
  90. var $file = false;
  91. /**
  92. * ID3 v1 tag found? (also true if v1.1 found)
  93. * @var boolean
  94. */
  95. var $id3v1 = false;
  96. /**
  97. * ID3 v1.1 tag found?
  98. * @var boolean
  99. */
  100. var $id3v11 = false;
  101. /**
  102. * ID3 v2 tag found? (not used yet)
  103. * @var boolean
  104. */
  105. var $id3v2 = false;
  106.  
  107. // ID3v1.1 Fields:
  108. /**
  109. * trackname
  110. * @var string
  111. */
  112. var $name = '';
  113. /**
  114. * artists
  115. * @var string
  116. */
  117. var $artists = '';
  118. /**
  119. * album
  120. * @var string
  121. */
  122. var $album = '';
  123. /**
  124. * year
  125. * @var string
  126. */
  127. var $year = '';
  128. /**
  129. * comment
  130. * @var string
  131. */
  132. var $comment = '';
  133. /**
  134. * track number
  135. * @var integer
  136. */
  137. var $track = 0;
  138. /**
  139. * genre name
  140. * @var string
  141. */
  142. var $genre = '';
  143. /**
  144. * genre number
  145. * @var integer
  146. */
  147. var $genreno = 255;
  148.  
  149. // MP3 Frame Stuff
  150. /**
  151. * Was the file studied to learn more info?
  152. * @var boolean
  153. */
  154. var $studied = false;
  155.  
  156. /**
  157. * version of mpeg
  158. * @var integer
  159. */
  160. var $mpeg_ver = 0;
  161. /**
  162. * version of layer
  163. * @var integer
  164. */
  165. var $layer = 0;
  166. /**
  167. * version of bitrate
  168. * @var integer
  169. */
  170. var $bitrate = 0;
  171. /**
  172. * Frames are crc protected?
  173. * @var boolean
  174. */
  175. var $crc = false;
  176. /**
  177. * frequency
  178. * @var integer
  179. */
  180. var $frequency = 0;
  181. /**
  182. * encoding type (CBR or VBR)
  183. * @var string
  184. */
  185. var $encoding_type = 0;
  186. /**
  187. * number of samples per MPEG audio frame
  188. * @var integer
  189. */
  190. var $samples_per_frame = 0;
  191. /**
  192. * samples in file
  193. * @var integer
  194. */
  195. var $samples = 0;
  196. /**
  197. * Bytes in file without tag overhead
  198. * @var integer
  199. */
  200. var $musicsize = -1;
  201. /**
  202. * number of MPEG audio frames
  203. * @var integer
  204. */
  205. var $frames = 0;
  206. /**
  207. * quality indicator (0% - 100%)
  208. * @var integer
  209. */
  210. var $quality = 0;
  211. /**
  212. * Frames padded
  213. * @var boolean
  214. */
  215. var $padding = false;
  216. /**
  217. * private bit set
  218. * @var boolean
  219. */
  220. var $private = false;
  221. /**
  222. * Mode (Stero etc)
  223. * @var string
  224. */
  225. var $mode = '';
  226. /**
  227. * Copyrighted
  228. * @var string
  229. */
  230. var $copyright = false;
  231. /**
  232. * On Original Media? (never used)
  233. * @var boolean
  234. */
  235. var $original = false;
  236. /**
  237. * Emphasis (also never used)
  238. * @var boolean
  239. */
  240. var $emphasis = '';
  241. /**
  242. * Bytes in file
  243. * @var integer
  244. */
  245. var $filesize = -1;
  246. /**
  247. * Byte at which the first mpeg header was found
  248. * @var integer
  249. */
  250. var $frameoffset = -1;
  251. /**
  252. * length of mp3 format hh:mm:ss
  253. * @var string
  254. */
  255. var $lengthh = false;
  256. /**
  257. * length of mp3 format mm:ss
  258. * @var string
  259. */
  260. var $length = false;
  261. /**
  262. * length of mp3 in seconds
  263. * @var string
  264. */
  265. var $lengths = false;
  266.  
  267. /**
  268. * if any errors they will be here
  269. * @var string
  270. */
  271. var $error = false;
  272.  
  273. /**
  274. * print debugging info?
  275. * @var boolean
  276. */
  277. var $debug = false;
  278. /**
  279. * print debugg
  280. * @var string
  281. */
  282. var $debugbeg = '<DIV STYLE="margin: 0.5 em; padding: 0.5 em; border-width: thin; border-color: black; border-style: solid">';
  283. /**
  284. * print debugg
  285. * @var string
  286. */
  287. var $debugend = '</DIV>';
  288.  
  289. /*
  290. * creates a new id3 object
  291. * and loads a tag from a file.
  292. *
  293. * @param string $study study the mpeg frame to get extra info like bitrate and frequency
  294. * You should advoid studing alot of files as it will siginficantly
  295. * slow this down.
  296. * @access public
  297. */
  298. function MP3_Id($study = false) {
  299. if(defined('ID3_SHOW_DEBUG')) $this->debug = true;
  300. $this->study=($study || defined('ID3_AUTO_STUDY'));
  301.  
  302. } // id3()
  303.  
  304. /**
  305. * reads the given file and parse it
  306. *
  307. * @param string $file the name of the file to parse
  308. * @return mixed PEAR_Error on error
  309. * @access public
  310. */
  311. function read( $file="") {
  312. if ($this->debug) print($this->debugbeg . "id3('$file')<HR>\n");
  313.  
  314. if(!empty($file))$this->file = $file;
  315. if ($this->debug) print($this->debugend);
  316.  
  317. return $this->_read_v1();
  318. }
  319.  
  320. /**
  321. * sets a field
  322. *
  323. * possible names of tags are:
  324. * artists - Name of band or artist
  325. * album - Name of the album
  326. * year - publishing year of the album or song
  327. * comment - song comment
  328. * track - the number of the track
  329. * genre - genre of the song
  330. * genreno - Number of the genre
  331. *
  332. * @param mixed $name Name of the tag to set or hash with the key as fieldname
  333. * @param mixed $value the value to set
  334. *
  335. * @access public
  336. */
  337. function setTag($name, $value) {
  338. if( is_array($name)) {
  339. foreach( $name as $n => $v) {
  340. $this -> $n = $v ;
  341. }
  342. } else {
  343. $this -> $name = $value ;
  344. }
  345. }
  346.  
  347. /**
  348. * get the value of a tag
  349. *
  350. * @param string $name the name of the field to get
  351. * @param mixed $default returned if the field not exists
  352. *
  353. * @return mixed The value of the field
  354. * @access public
  355. * @see setTag
  356. */
  357. function getTag($name, $default = 0) {
  358. if(empty($this -> $name)) {
  359. return $default ;
  360. } else {
  361. return $this -> $name ;
  362. }
  363. }
  364.  
  365. /**
  366. * update the id3v1 tags on the file.
  367. * Note: If/when ID3v2 is implemented this method will probably get another
  368. * parameters.
  369. *
  370. * @param boolean $v1 if true update/create an id3v1 tag on the file. (defaults to true)
  371. *
  372. * @access public
  373. */
  374. function write($v1 = true) {
  375. if ($this->debug) print($this->debugbeg . "write()<HR>\n");
  376. if ($v1) {
  377. $this->_write_v1();
  378. }
  379. if ($this->debug) print($this->debugend);
  380. } // write()
  381.  
  382. /**
  383. * study() - does extra work to get the MPEG frame info.
  384. *
  385. * @access public
  386. */
  387. function study() {
  388. $this->studied = true;
  389. $this->_readframe();
  390. } // study()
  391.  
  392. /**
  393. * copy($from) - set's the ID3 fields to the same as the fields in $from
  394. *
  395. * @param string $from fields to copy
  396. * @access public
  397. */
  398. function copy($from) {
  399. if ($this->debug) print($this->debugbeg . "copy(\$from)<HR>\n");
  400. $this->name = $from->name;
  401. $this->artists = $from->artists;
  402. $this->album = $from->album;
  403. $this->year = $from->year;
  404. $this->comment = $from->comment;
  405. $this->track = $from->track;
  406. $this->genre = $from->genre;
  407. $this->genreno = $from->genreno;
  408. if ($this->debug) print($this->debugend);
  409. } // copy($from)
  410.  
  411. /**
  412. * remove - removes the id3 tag(s) from a file.
  413. *
  414. * @param boolean $id3v1 true to remove the tag
  415. * @param boolean $id3v2 true to remove the tag (Not yet implemented)
  416. *
  417. * @access public
  418. */
  419. function remove($id3v1 = true, $id3v2 = true) {
  420. if ($this->debug) print($this->debugbeg . "remove()<HR>\n");
  421.  
  422. if ($id3v1) {
  423. $this->_remove_v1();
  424. }
  425.  
  426. if ($id3v2) {
  427. // TODO: write ID3v2 code
  428. }
  429.  
  430. if ($this->debug) print($this->debugend);
  431. } // remove
  432.  
  433.  
  434. /**
  435. * read a ID3 v1 or v1.1 tag from a file
  436. *
  437. * $file should be the path to the mp3 to look for a tag.
  438. * When in doubt use the full path.
  439. *
  440. * @return mixed PEAR_Error if fails
  441. * @access private
  442. */
  443. function _read_v1() {
  444. if ($this->debug) print($this->debugbeg . "_read_v1()<HR>\n");
  445.  
  446. $mqr = get_magic_quotes_runtime();
  447. set_magic_quotes_runtime(0);
  448.  
  449. if (! ($f = @fopen($this->file, 'rb')) ) {
  450. return PEAR::raiseError( "Unable to open " . $this->file, PEAR_MP3_ID_FNO);
  451. }
  452.  
  453. if (fseek($f, -128, SEEK_END) == -1) {
  454. return PEAR::raiseError( 'Unable to see to end - 128 of ' . $this->file, PEAR_MP3_ID_RE);
  455. }
  456.  
  457. $r = fread($f, 128);
  458. fclose($f);
  459. set_magic_quotes_runtime($mqr);
  460.  
  461. if ($this->debug) {
  462. $unp = unpack('H*raw', $r);
  463. print_r($unp);
  464. }
  465.  
  466. $id3tag = $this->_decode_v1($r);
  467.  
  468. if(!PEAR::isError( $id3tag)) {
  469. $this->id3v1 = true;
  470.  
  471. $tmp = explode(Chr(0), $id3tag['NAME']);
  472. $this->name = $tmp[0];
  473.  
  474. $tmp = explode(Chr(0), $id3tag['ARTISTS']);
  475. $this->artists = $tmp[0];
  476.  
  477. $tmp = explode(Chr(0), $id3tag['ALBUM']);
  478. $this->album = $tmp[0];
  479.  
  480. $tmp = explode(Chr(0), $id3tag['YEAR']);
  481. $this->year = $tmp[0];
  482.  
  483. $tmp = explode(Chr(0), $id3tag['COMMENT']);
  484. $this->comment = $tmp[0];
  485.  
  486. if (isset($id3tag['TRACK'])) {
  487. $this->id3v11 = true;
  488. $this->track = $id3tag['TRACK'];
  489. }
  490.  
  491. $this->genreno = $id3tag['GENRENO'];
  492. $this->genre = $id3tag['GENRE'];
  493. } else {
  494. return $id3tag ;
  495. }
  496.  
  497. if ($this->debug) print($this->debugend);
  498. } // _read_v1()
  499.  
  500. /**
  501. * decodes that ID3v1 or ID3v1.1 tag
  502. *
  503. * false will be returned if there was an error decoding the tag
  504. * else an array will be returned
  505. *
  506. * @param string $rawtag tag to decode
  507. * @return string decoded tag
  508. * @access private
  509. */
  510. function _decode_v1($rawtag) {
  511. if ($this->debug) print($this->debugbeg . "_decode_v1(\$rawtag)<HR>\n");
  512.  
  513. if ($rawtag[125] == Chr(0) and $rawtag[126] != Chr(0)) {
  514. // ID3 v1.1
  515. $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
  516. } else {
  517. // ID3 v1
  518. $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
  519. }
  520.  
  521. $id3tag = unpack($format, $rawtag);
  522. if ($this->debug) print_r($id3tag);
  523.  
  524. if ($id3tag['TAG'] == 'TAG') {
  525. $id3tag['GENRE'] = $this->getgenre($id3tag['GENRENO']);
  526. } else {
  527. $id3tag = PEAR::raiseError( 'TAG not found', PEAR_MP3_ID_TNF);
  528. }
  529. if ($this->debug) print($this->debugend);
  530. return $id3tag;
  531. } // _decode_v1()
  532.  
  533.  
  534. /**
  535. * writes a ID3 v1 or v1.1 tag to a file
  536. *
  537. * @return mixed returns PEAR_Error when fails
  538. * @access private
  539. */
  540. function _write_v1() {
  541. if ($this->debug) print($this->debugbeg . "_write_v1()<HR>\n");
  542.  
  543. $file = $this->file;
  544.  
  545. if (! ($f = @fopen($file, 'r+b')) ) {
  546. return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO);
  547. }
  548.  
  549. if (fseek($f, -128, SEEK_END) == -1) {
  550. // $this->error = 'Unable to see to end - 128 of ' . $file;
  551. return PEAR::raiseError( "Unable to see to end - 128 of " . $file, PEAR_MP3_ID_RE);
  552. }
  553.  
  554. $this->genreno = $this->getgenreno($this->genre, $this->genreno);
  555.  
  556. $newtag = $this->_encode_v1();
  557.  
  558. $mqr = get_magic_quotes_runtime();
  559. set_magic_quotes_runtime(0);
  560.  
  561. $r = fread($f, 128);
  562.  
  563. if ( !PEAR::isError( $this->_decode_v1($r))) {
  564. if (fseek($f, -128, SEEK_END) == -1) {
  565. // $this->error = 'Unable to see to end - 128 of ' . $file;
  566. return PEAR::raiseError( "Unable to see to end - 128 of " . $file, PEAR_MP3_ID_RE);
  567. }
  568. fwrite($f, $newtag);
  569. } else {
  570. if (fseek($f, 0, SEEK_END) == -1) {
  571. // $this->error = 'Unable to see to end of ' . $file;
  572. return PEAR::raiseError( "Unable to see to end of " . $file, PEAR_MP3_ID_RE);
  573. }
  574. fwrite($f, $newtag);
  575. }
  576. fclose($f);
  577. set_magic_quotes_runtime($mqr);
  578.  
  579. if ($this->debug) print($this->debugend);
  580. } // _write_v1()
  581.  
  582. /*
  583. * encode the ID3 tag
  584. *
  585. * the newly built tag will be returned
  586. *
  587. * @return string the new tag
  588. * @access private
  589. */
  590. function _encode_v1() {
  591. if ($this->debug) print($this->debugbeg . "_encode_v1()<HR>\n");
  592.  
  593. if ($this->track) {
  594. // ID3 v1.1
  595. $id3pack = 'a3a30a30a30a4a28x1C1C1';
  596. $newtag = pack($id3pack,
  597. 'TAG',
  598. $this->name,
  599. $this->artists,
  600. $this->album,
  601. $this->year,
  602. $this->comment,
  603. $this->track,
  604. $this->genreno
  605. );
  606. } else {
  607. // ID3 v1
  608. $id3pack = 'a3a30a30a30a4a30C1';
  609. $newtag = pack($id3pack,
  610. 'TAG',
  611. $this->name,
  612. $this->artists,
  613. $this->album,
  614. $this->year,
  615. $this->comment,
  616. $this->genreno
  617. );
  618. }
  619.  
  620. if ($this->debug) {
  621. print('id3pack: ' . $id3pack . "\n");
  622. $unp = unpack('H*new', $newtag);
  623. print_r($unp);
  624. }
  625.  
  626. if ($this->debug) print($this->debugend);
  627. return $newtag;
  628. } // _encode_v1()
  629.  
  630. /**
  631. * if exists it removes an ID3v1 or v1.1 tag
  632. *
  633. * returns true if the tag was removed or none was found
  634. * else false if there was an error
  635. *
  636. * @return boolean true, if the tag was removed
  637. * @access private
  638. */
  639. function _remove_v1() {
  640. if ($this->debug) print($this->debugbeg . "_remove_v1()<HR>\n");
  641.  
  642. $file = $this->file;
  643.  
  644. if (! ($f = fopen($file, 'r+b')) ) {
  645. return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO);
  646. }
  647.  
  648. if (fseek($f, -128, SEEK_END) == -1) {
  649. return PEAR::raiseError( 'Unable to see to end - 128 of ' . $file, PEAR_MP3_ID_RE);
  650. }
  651.  
  652. $mqr = get_magic_quotes_runtime();
  653. set_magic_quotes_runtime(0);
  654.  
  655. $r = fread($f, 128);
  656.  
  657. $success = false;
  658. if ( !PEAR::isError( $this->_decode_v1($r))) {
  659. $size = filesize($this->file) - 128;
  660. if ($this->debug) print('size: old: ' . filesize($this->file));
  661. $success = ftruncate($f, $size);
  662. clearstatcache();
  663. if ($this->debug) print(' new: ' . filesize($this->file));
  664. }
  665. fclose($f);
  666. set_magic_quotes_runtime($mqr);
  667.  
  668. if ($this->debug) print($this->debugend);
  669. return $success;
  670. } // _remove_v1()
  671.  
  672. /**
  673. * reads a frame from the file
  674. *
  675. * @return mixed PEAR_Error when fails
  676. * @access private
  677. */
  678. function _readframe() {
  679. if ($this->debug) print($this->debugbeg . "_readframe()<HR>\n");
  680.  
  681. $file = $this->file;
  682.  
  683. $mqr = get_magic_quotes_runtime();
  684. set_magic_quotes_runtime(0);
  685.  
  686. if (! ($f = fopen($file, 'rb')) ) {
  687. if ($this->debug) print($this->debugend);
  688. return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO) ;
  689. }
  690.  
  691. $this->filesize = filesize($file);
  692.  
  693. do {
  694. while (fread($f,1) != Chr(255)) { // Find the first frame
  695. if ($this->debug) echo "Find...\n";
  696. if (feof($f)) {
  697. if ($this->debug) print($this->debugend);
  698. return PEAR::raiseError( "No mpeg frame found", PEAR_MP3_ID_NOMP3) ;
  699. }
  700. }
  701. fseek($f, ftell($f) - 1); // back up one byte
  702.  
  703. $frameoffset = ftell($f);
  704.  
  705. $r = fread($f, 4);
  706. // Binary to Hex to a binary sting. ugly but best I can think of.
  707. // $bits = unpack('H*bits', $r);
  708. // $bits = base_convert($bits['bits'],16,2);
  709. $bits = sprintf("%'08b%'08b%'08b%'08b", ord($r{0}), ord($r{1}), ord($r{2}), ord($r{3}));
  710. } while (!$bits[8] and !$bits[9] and !$bits[10]); // 1st 8 bits true from the while
  711. if ($this->debug) print('Bits: ' . $bits . "\n");
  712.  
  713. $this->frameoffset = $frameoffset;
  714.  
  715. // Detect VBR header
  716. if ($bits[11] == 0) {
  717. if (($bits[24] == 1) && ($bits[25] == 1)) {
  718. $vbroffset = 9; // MPEG 2.5 Mono
  719. } else {
  720. $vbroffset = 17; // MPEG 2.5 Stereo
  721. }
  722. } else if ($bits[12] == 0) {
  723. if (($bits[24] == 1) && ($bits[25] == 1)) {
  724. $vbroffset = 9; // MPEG 2 Mono
  725. } else {
  726. $vbroffset = 17; // MPEG 2 Stereo
  727. }
  728. } else {
  729. if (($bits[24] == 1) && ($bits[25] == 1)) {
  730. $vbroffset = 17; // MPEG 1 Mono
  731. } else {
  732. $vbroffset = 32; // MPEG 1 Stereo
  733. }
  734. }
  735.  
  736. fseek($f, ftell($f) + $vbroffset);
  737. $r = fread($f, 4);
  738.  
  739. switch ($r) {
  740. case 'Xing':
  741. $this->encoding_type = 'VBR';
  742. case 'Info':
  743. // Extract info from Xing header
  744.  
  745. if ($this->debug) print('Encoding Header: ' . $r . "\n");
  746.  
  747. $r = fread($f, 4);
  748. $vbrbits = sprintf("%'08b", ord($r{3}));
  749.  
  750. if ($this->debug) print('XING Header Bits: ' . $vbrbits . "\n");
  751.  
  752. if ($vbrbits[7] == 1) {
  753. // Next 4 bytes contain number of frames
  754. $r = fread($f, 4);
  755. $this->frames = unpack('N', $r);
  756. $this->frames = $this->frames[1];
  757. }
  758.  
  759. if ($vbrbits[6] == 1) {
  760. // Next 4 bytes contain number of bytes
  761. $r = fread($f, 4);
  762. $this->musicsize = unpack('N', $r);
  763. $this->musicsize = $this->musicsize[1];
  764. }
  765.  
  766. if ($vbrbits[5] == 1) {
  767. // Next 100 bytes contain TOC entries, skip
  768. fseek($f, ftell($f) + 100);
  769. }
  770.  
  771. if ($vbrbits[4] == 1) {
  772. // Next 4 bytes contain Quality Indicator
  773. $r = fread($f, 4);
  774. $this->quality = unpack('N', $r);
  775. $this->quality = $this->quality[1];
  776. }
  777.  
  778. break;
  779.  
  780. case 'VBRI':
  781. default:
  782. if ($vbroffset != 32) {
  783. // VBRI Header is fixed after 32 bytes, so maybe we are looking at the wrong place.
  784. fseek($f, ftell($f) + 32 - $vbroffset);
  785. $r = fread($f, 4);
  786.  
  787. if ($r != 'VBRI') {
  788. $this->encoding_type = 'CBR';
  789. break;
  790. }
  791. } else {
  792. $this->encoding_type = 'CBR';
  793. break;
  794. }
  795.  
  796. if ($this->debug) print('Encoding Header: ' . $r . "\n");
  797.  
  798. $this->encoding_type = 'VBR';
  799.  
  800. // Next 2 bytes contain Version ID, skip
  801. fseek($f, ftell($f) + 2);
  802.  
  803. // Next 2 bytes contain Delay, skip
  804. fseek($f, ftell($f) + 2);
  805.  
  806. // Next 2 bytes contain Quality Indicator
  807. $r = fread($f, 2);
  808. $this->quality = unpack('n', $r);
  809. $this->quality = $this->quality[1];
  810.  
  811. // Next 4 bytes contain number of bytes
  812. $r = fread($f, 4);
  813. $this->musicsize = unpack('N', $r);
  814. $this->musicsize = $this->musicsize[1];
  815.  
  816. // Next 4 bytes contain number of frames
  817. $r = fread($f, 4);
  818. $this->frames = unpack('N', $r);
  819. $this->frames = $this->frames[1];
  820. }
  821.  
  822. fclose($f);
  823. set_magic_quotes_runtime($mqr);
  824.  
  825. if ($bits[11] == 0) {
  826. $this->mpeg_ver = "2.5";
  827. $bitrates = array(
  828. '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
  829. '2' => array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  830. '3' => array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  831. );
  832. } else if ($bits[12] == 0) {
  833. $this->mpeg_ver = "2";
  834. $bitrates = array(
  835. '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
  836. '2' => array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  837. '3' => array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  838. );
  839. } else {
  840. $this->mpeg_ver = "1";
  841. $bitrates = array(
  842. '1' => array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0),
  843. '2' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
  844. '3' => array(0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0),
  845. );
  846. }
  847. if ($this->debug) print('MPEG' . $this->mpeg_ver . "\n");
  848.  
  849. $layer = array(
  850. array(0,3),
  851. array(2,1),
  852. );
  853. $this->layer = $layer[$bits[13]][$bits[14]];
  854. if ($this->debug) print('layer: ' . $this->layer . "\n");
  855.  
  856. if ($bits[15] == 0) {
  857. // It's backwards, if the bit is not set then it is protected.
  858. if ($this->debug) print("protected (crc)\n");
  859. $this->crc = true;
  860. }
  861.  
  862. $bitrate = 0;
  863. if ($bits[16] == 1) $bitrate += 8;
  864. if ($bits[17] == 1) $bitrate += 4;
  865. if ($bits[18] == 1) $bitrate += 2;
  866. if ($bits[19] == 1) $bitrate += 1;
  867. $this->bitrate = $bitrates[$this->layer][$bitrate];
  868.  
  869. $frequency = array(
  870. '1' => array(
  871. '0' => array(44100, 48000),
  872. '1' => array(32000, 0),
  873. ),
  874. '2' => array(
  875. '0' => array(22050, 24000),
  876. '1' => array(16000, 0),
  877. ),
  878. '2.5' => array(
  879. '0' => array(11025, 12000),
  880. '1' => array(8000, 0),
  881. ),
  882. );
  883. $this->frequency = $frequency[$this->mpeg_ver][$bits[20]][$bits[21]];
  884.  
  885. $this->padding = $bits[22];
  886. $this->private = $bits[23];
  887.  
  888. $mode = array(
  889. array('Stereo', 'Joint Stereo'),
  890. array('Dual Channel', 'Mono'),
  891. );
  892. $this->mode = $mode[$bits[24]][$bits[25]];
  893.  
  894. // XXX: I dunno what the mode extension is for bits 26,27
  895.  
  896. $this->copyright = $bits[28];
  897. $this->original = $bits[29];
  898.  
  899. $emphasis = array(
  900. array('none', '50/15ms'),
  901. array('', 'CCITT j.17'),
  902. );
  903. $this->emphasis = $emphasis[$bits[30]][$bits[31]];
  904.  
  905. $samplesperframe = array(
  906. '1' => array(
  907. '1' => 384,
  908. '2' => 1152,
  909. '3' => 1152
  910. ),
  911. '2' => array(
  912. '1' => 384,
  913. '2' => 1152,
  914. '3' => 576
  915. ),
  916. '2.5' => array(
  917. '1' => 384,
  918. '2' => 1152,
  919. '3' => 576
  920. ),
  921. );
  922. $this->samples_per_frame = $samplesperframe[$this->mpeg_ver][$this->layer];
  923.  
  924. if ($this->encoding_type != 'VBR') {
  925. if ($this->bitrate == 0) {
  926. $s = -1;
  927. } else {
  928. $s = ((8*filesize($this->file))/1000) / $this->bitrate;
  929. }
  930. $this->length = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
  931. $this->lengthh = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
  932. $this->lengths = (int)$s;
  933.  
  934. $this->samples = ceil($this->lengths * $this->frequency);
  935. if(0 != $this->samples_per_frame) {
  936. $this->frames = ceil($this->samples / $this->samples_per_frame);
  937. } else {
  938. $this->frames = 0;
  939. }
  940. $this->musicsize = ceil($this->lengths * $this->bitrate * 1000 / 8);
  941. } else {
  942. $this->samples = $this->samples_per_frame * $this->frames;
  943. $s = $this->samples / $this->frequency;
  944.  
  945. $this->length = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
  946. $this->lengthh = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
  947. $this->lengths = (int)$s;
  948.  
  949. $this->bitrate = (int)(($this->musicsize / $s) * 8 / 1000);
  950. }
  951.  
  952. if ($this->debug) print($this->debugend);
  953. } // _readframe()
  954.  
  955. /**
  956. * getGenre - return the name of a genre number
  957. *
  958. * if no genre number is specified the genre number from
  959. * $this->genreno will be used.
  960. *
  961. * the genre is returned or false if an error or not found
  962. * no error message is ever returned
  963. *
  964. * @param integer $genreno Number of the genre
  965. * @return mixed false, if no genre found, else string
  966. *
  967. * @access public
  968. */
  969. function getGenre($genreno) {
  970. if ($this->debug) print($this->debugbeg . "getgenre($genreno)<HR>\n");
  971.  
  972. $genres = $this->genres();
  973. if (isset($genres[$genreno])) {
  974. $genre = $genres[$genreno];
  975. if ($this->debug) print($genre . "\n");
  976. } else {
  977. $genre = '';
  978. }
  979.  
  980. if ($this->debug) print($this->debugend);
  981. return $genre;
  982. } // getGenre($genreno)
  983.  
  984. /*
  985. * getGenreNo - return the number of the genre name
  986. *
  987. * the genre number is returned or 0xff (255) if a match is not found
  988. * you can specify the default genreno to use if one is not found
  989. * no error message is ever returned
  990. *
  991. * @param string $genre Name of the genre
  992. * @param integer $default Genre number in case of genre not found
  993. *
  994. * @access public
  995. */
  996. function getGenreNo($genre, $default = 0xff) {
  997. if ($this->debug) print($this->debugbeg . "getgenreno('$genre',$default)<HR>\n");
  998.  
  999. $genres = $this->genres();
  1000. $genreno = false;
  1001. if ($genre) {
  1002. foreach ($genres as $no => $name) {
  1003. if (strtolower($genre) == strtolower($name)) {
  1004. if ($this->debug) print("$no:'$name' == '$genre'");
  1005. $genreno = $no;
  1006. }
  1007. }
  1008. }
  1009. if ($genreno === false) $genreno = $default;
  1010. if ($this->debug) print($this->debugend);
  1011. return $genreno;
  1012. } // getGenreNo($genre, $default = 0xff)
  1013.  
  1014. /*
  1015. * genres - returns an array of the ID3v1 genres
  1016. *
  1017. * @return array
  1018. *
  1019. * @access public
  1020. */
  1021. function genres() {
  1022. return array(
  1023. 0 => 'Blues',
  1024. 1 => 'Classic Rock',
  1025. 2 => 'Country',
  1026. 3 => 'Dance',
  1027. 4 => 'Disco',
  1028. 5 => 'Funk',
  1029. 6 => 'Grunge',
  1030. 7 => 'Hip-Hop',
  1031. 8 => 'Jazz',
  1032. 9 => 'Metal',
  1033. 10 => 'New Age',
  1034. 11 => 'Oldies',
  1035. 12 => 'Other',
  1036. 13 => 'Pop',
  1037. 14 => 'R&B',
  1038. 15 => 'Rap',
  1039. 16 => 'Reggae',
  1040. 17 => 'Rock',
  1041. 18 => 'Techno',
  1042. 19 => 'Industrial',
  1043. 20 => 'Alternative',
  1044. 21 => 'Ska',
  1045. 22 => 'Death Metal',
  1046. 23 => 'Pranks',
  1047. 24 => 'Soundtrack',
  1048. 25 => 'Euro-Techno',
  1049. 26 => 'Ambient',
  1050. 27 => 'Trip-Hop',
  1051. 28 => 'Vocal',
  1052. 29 => 'Jazz+Funk',
  1053. 30 => 'Fusion',
  1054. 31 => 'Trance',
  1055. 32 => 'Classical',
  1056. 33 => 'Instrumental',
  1057. 34 => 'Acid',
  1058. 35 => 'House',
  1059. 36 => 'Game',
  1060. 37 => 'Sound Clip',
  1061. 38 => 'Gospel',
  1062. 39 => 'Noise',
  1063. 40 => 'Alternative Rock',
  1064. 41 => 'Bass',
  1065. 42 => 'Soul',
  1066. 43 => 'Punk',
  1067. 44 => 'Space',
  1068. 45 => 'Meditative',
  1069. 46 => 'Instrumental Pop',
  1070. 47 => 'Instrumental Rock',
  1071. 48 => 'Ethnic',
  1072. 49 => 'Gothic',
  1073. 50 => 'Darkwave',
  1074. 51 => 'Techno-Industrial',
  1075. 52 => 'Electronic',
  1076. 53 => 'Pop-Folk',
  1077. 54 => 'Eurodance',
  1078. 55 => 'Dream',
  1079. 56 => 'Southern Rock',
  1080. 57 => 'Comedy',
  1081. 58 => 'Cult',
  1082. 59 => 'Gangsta',
  1083. 60 => 'Top 40',
  1084. 61 => 'Christian Rap',
  1085. 62 => 'Pop/Funk',
  1086. 63 => 'Jungle',
  1087. 64 => 'Native US',
  1088. 65 => 'Cabaret',
  1089. 66 => 'New Wave',
  1090. 67 => 'Psychadelic',
  1091. 68 => 'Rave',
  1092. 69 => 'Showtunes',
  1093. 70 => 'Trailer',
  1094. 71 => 'Lo-Fi',
  1095. 72 => 'Tribal',
  1096. 73 => 'Acid Punk',
  1097. 74 => 'Acid Jazz',
  1098. 75 => 'Polka',
  1099. 76 => 'Retro',
  1100. 77 => 'Musical',
  1101. 78 => 'Rock & Roll',
  1102. 79 => 'Hard Rock',
  1103. 80 => 'Folk',
  1104. 81 => 'Folk-Rock',
  1105. 82 => 'National Folk',
  1106. 83 => 'Swing',
  1107. 84 => 'Fast Fusion',
  1108. 85 => 'Bebob',
  1109. 86 => 'Latin',
  1110. 87 => 'Revival',
  1111. 88 => 'Celtic',
  1112. 89 => 'Bluegrass',
  1113. 90 => 'Avantgarde',
  1114. 91 => 'Gothic Rock',
  1115. 92 => 'Progressive Rock',
  1116. 93 => 'Psychedelic Rock',
  1117. 94 => 'Symphonic Rock',
  1118. 95 => 'Slow Rock',
  1119. 96 => 'Big Band',
  1120. 97 => 'Chorus',
  1121. 98 => 'Easy Listening',
  1122. 99 => 'Acoustic',
  1123. 100 => 'Humour',
  1124. 101 => 'Speech',
  1125. 102 => 'Chanson',
  1126. 103 => 'Opera',
  1127. 104 => 'Chamber Music',
  1128. 105 => 'Sonata',
  1129. 106 => 'Symphony',
  1130. 107 => 'Booty Bass',
  1131. 108 => 'Primus',
  1132. 109 => 'Porn Groove',
  1133. 110 => 'Satire',
  1134. 111 => 'Slow Jam',
  1135. 112 => 'Club',
  1136. 113 => 'Tango',
  1137. 114 => 'Samba',
  1138. 115 => 'Folklore',
  1139. 116 => 'Ballad',
  1140. 117 => 'Power Ballad',
  1141. 118 => 'Rhytmic Soul',
  1142. 119 => 'Freestyle',
  1143. 120 => 'Duet',
  1144. 121 => 'Punk Rock',
  1145. 122 => 'Drum Solo',
  1146. 123 => 'Acapella',
  1147. 124 => 'Euro-House',
  1148. 125 => 'Dance Hall',
  1149. 126 => 'Goa',
  1150. 127 => 'Drum & Bass',
  1151. 128 => 'Club-House',
  1152. 129 => 'Hardcore',
  1153. 130 => 'Terror',
  1154. 131 => 'Indie',
  1155. 132 => 'BritPop',
  1156. 133 => 'Negerpunk',
  1157. 134 => 'Polsk Punk',
  1158. 135 => 'Beat',
  1159. 136 => 'Christian Gangsta Rap',
  1160. 137 => 'Heavy Metal',
  1161. 138 => 'Black Metal',
  1162. 139 => 'Crossover',
  1163. 140 => 'Contemporary Christian',
  1164. 141 => 'Christian Rock',
  1165. 142 => 'Merengue',
  1166. 143 => 'Salsa',
  1167. 144 => 'Trash Metal',
  1168. 145 => 'Anime',
  1169. 146 => 'Jpop',
  1170. 147 => 'Synthpop'
  1171. );
  1172. } // genres
  1173. } // end of id3
  1174.  
  1175. ?>