home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / MP3 / Id.php
Encoding:
PHP Script  |  2005-12-02  |  31.2 KB  |  1,176 lines

  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. ?>
  1176.