MP3_Id
[ class tree: MP3_Id ] [ index: MP3_Id ] [ all elements ]

Source for file Id.php

Documentation is available at Id.php

  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->debugprint($this->debugbeg . "id3('$file')<HR>\n");
  313.  
  314.         if(!empty($file))$this->file = $file;
  315.         if ($this->debugprint($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.         ifis_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->debugprint($this->debugbeg . "write()<HR>\n");
  376.     if ($v1{
  377.         $this->_write_v1();
  378.     }
  379.     if ($this->debugprint($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->debugprint($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->debugprint($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->debugprint($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->debugprint($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->debugprint($this->debugbeg . "_read_v1()<HR>\n");
  445.  
  446.     $mqr get_magic_quotes_runtime();
  447.  
  448.     if (($f @fopen($this->file'rb')) ) {
  449.         return PEAR::raiseError"Unable to open " $this->filePEAR_MP3_ID_FNO);
  450.     }
  451.  
  452.     if (fseek($f-128SEEK_END== -1{
  453.         return PEAR::raiseError'Unable to see to end - 128 of ' $this->filePEAR_MP3_ID_RE);
  454.     }
  455.  
  456.     $r fread($f128);
  457.     fclose($f);
  458.  
  459.     if ($this->debug{
  460.         $unp unpack('H*raw'$r);
  461.         print_r($unp);
  462.     }
  463.  
  464.     $id3tag $this->_decode_v1($r);
  465.  
  466.     if(!PEAR::isError$id3tag)) {
  467.         $this->id3v1 = true;
  468.  
  469.         $tmp explode(Chr(0)$id3tag['NAME']);
  470.         $this->name = $tmp[0];
  471.  
  472.         $tmp explode(Chr(0)$id3tag['ARTISTS']);
  473.         $this->artists = $tmp[0];
  474.  
  475.         $tmp explode(Chr(0)$id3tag['ALBUM']);
  476.         $this->album = $tmp[0];
  477.  
  478.         $tmp explode(Chr(0)$id3tag['YEAR']);
  479.         $this->year = $tmp[0];
  480.  
  481.         $tmp explode(Chr(0)$id3tag['COMMENT']);
  482.         $this->comment = $tmp[0];
  483.  
  484.         if (isset($id3tag['TRACK'])) {
  485.         $this->id3v11 = true;
  486.         $this->track = $id3tag['TRACK'];
  487.         }
  488.  
  489.         $this->genreno = $id3tag['GENRENO'];
  490.         $this->genre = $id3tag['GENRE'];
  491.     else {
  492.         return $id3tag ;
  493.         }
  494.  
  495.     if ($this->debugprint($this->debugend);
  496.     // _read_v1()
  497.  
  498.     /**
  499.      * decodes that ID3v1 or ID3v1.1 tag
  500.      *
  501.      * false will be returned if there was an error decoding the tag
  502.      * else an array will be returned
  503.      *
  504.      * @param   string  $rawtag    tag to decode
  505.      * @return  string  decoded tag
  506.      * @access  private
  507.      */
  508.     function _decode_v1($rawtag{
  509.     if ($this->debugprint($this->debugbeg . "_decode_v1(\$rawtag)<HR>\n");
  510.  
  511.     if ($rawtag[125== Chr(0and $rawtag[126!= Chr(0)) {
  512.         // ID3 v1.1
  513.         $format 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
  514.     else {
  515.         // ID3 v1
  516.         $format 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
  517.     }
  518.  
  519.     $id3tag unpack($format$rawtag);
  520.     if ($this->debugprint_r($id3tag);
  521.  
  522.     if ($id3tag['TAG'== 'TAG'{
  523.         $id3tag['GENRE'$this->getgenre($id3tag['GENRENO']);
  524.     else {
  525.         $id3tag = PEAR::raiseError'TAG not found'PEAR_MP3_ID_TNF);
  526.     }
  527.     if ($this->debugprint($this->debugend);
  528.     return $id3tag;
  529.     // _decode_v1()
  530.  
  531.  
  532.     /**
  533.      * writes a ID3 v1 or v1.1 tag to a file
  534.      *
  535.      * @return mixed    returns PEAR_Error when fails
  536.      * @access private
  537.      */
  538.     function _write_v1({
  539.     if ($this->debugprint($this->debugbeg . "_write_v1()<HR>\n");
  540.  
  541.     $file $this->file;
  542.  
  543.     if (($f @fopen($file'r+b')) ) {
  544.         return PEAR::raiseError"Unable to open " $filePEAR_MP3_ID_FNO);
  545.     }
  546.  
  547.     if (fseek($f-128SEEK_END== -1{
  548. //        $this->error = 'Unable to see to end - 128 of ' . $file;
  549.         return PEAR::raiseError"Unable to see to end - 128 of " $filePEAR_MP3_ID_RE);
  550.     }
  551.  
  552.     $this->genreno = $this->getgenreno($this->genre$this->genreno);
  553.  
  554.     $newtag $this->_encode_v1();
  555.     
  556.     $mqr get_magic_quotes_runtime();
  557.  
  558.     $r fread($f128);
  559.  
  560.     if !PEAR::isError$this->_decode_v1($r))) {
  561.         if (fseek($f-128SEEK_END== -1{
  562. //        $this->error = 'Unable to see to end - 128 of ' . $file;
  563.         return PEAR::raiseError"Unable to see to end - 128 of " $filePEAR_MP3_ID_RE);
  564.         }
  565.         fwrite($f$newtag);
  566.     else {
  567.         if (fseek($f0SEEK_END== -1{
  568. //        $this->error = 'Unable to see to end of ' . $file;
  569.         return PEAR::raiseError"Unable to see to end of " $filePEAR_MP3_ID_RE);
  570.         }
  571.         fwrite($f$newtag);
  572.     }
  573.     fclose($f);
  574.  
  575.     if ($this->debugprint($this->debugend);
  576.     // _write_v1()
  577.  
  578.     /*
  579.      * encode the ID3 tag
  580.      *
  581.      * the newly built tag will be returned
  582.      *
  583.      * @return string the new tag
  584.      * @access private
  585.      */
  586.     function _encode_v1({
  587.     if ($this->debugprint($this->debugbeg . "_encode_v1()<HR>\n");
  588.  
  589.     if ($this->track{
  590.         // ID3 v1.1
  591.         $id3pack 'a3a30a30a30a4a28x1C1C1';
  592.         $newtag pack($id3pack,
  593.             'TAG',
  594.             $this->name,
  595.             $this->artists,
  596.             $this->album,
  597.             $this->year,
  598.             $this->comment,
  599.             $this->track,
  600.             $this->genreno
  601.               );
  602.     else {
  603.         // ID3 v1
  604.         $id3pack 'a3a30a30a30a4a30C1';
  605.         $newtag pack($id3pack,
  606.             'TAG',
  607.             $this->name,
  608.             $this->artists,
  609.             $this->album,
  610.             $this->year,
  611.             $this->comment,
  612.             $this->genreno
  613.               );
  614.     }
  615.  
  616.     if ($this->debug{
  617.         print('id3pack: ' $id3pack "\n");
  618.         $unp unpack('H*new'$newtag);
  619.         print_r($unp);
  620.     }
  621.  
  622.     if ($this->debugprint($this->debugend);
  623.     return $newtag;
  624.     // _encode_v1()
  625.  
  626.     /**
  627.      * if exists it removes an ID3v1 or v1.1 tag
  628.      *
  629.      * returns true if the tag was removed or none was found
  630.      * else false if there was an error
  631.      *
  632.      * @return boolean true, if the tag was removed
  633.      * @access private
  634.      */
  635.     function _remove_v1({
  636.     if ($this->debugprint($this->debugbeg . "_remove_v1()<HR>\n");
  637.  
  638.     $file $this->file;
  639.  
  640.     if (($f fopen($file'r+b')) ) {
  641.         return PEAR::raiseError"Unable to open " $filePEAR_MP3_ID_FNO);
  642.     }
  643.  
  644.     if (fseek($f-128SEEK_END== -1{
  645.         return PEAR::raiseError'Unable to see to end - 128 of ' $filePEAR_MP3_ID_RE);
  646.     }
  647.  
  648.     $mqr get_magic_quotes_runtime();
  649.  
  650.     $r fread($f128);
  651.  
  652.     $success = false;
  653.     if !PEAR::isError$this->_decode_v1($r))) {
  654.         $size filesize($this->file- 128;
  655.         if ($this->debugprint('size: old: ' filesize($this->file));
  656.         $success ftruncate($f$size);
  657.         clearstatcache();
  658.         if ($this->debugprint(' new: ' filesize($this->file));
  659.     }
  660.     fclose($f);
  661.  
  662.     if ($this->debugprint($this->debugend);
  663.     return $success;
  664.     // _remove_v1()
  665.  
  666.     /**
  667.     * reads a frame from the file
  668.     *
  669.     * @return mixed PEAR_Error when fails
  670.     * @access private
  671.     */
  672.     function _readframe({
  673.     if ($this->debugprint($this->debugbeg . "_readframe()<HR>\n");
  674.  
  675.     $file $this->file;
  676.  
  677.     $mqr get_magic_quotes_runtime();
  678.  
  679.     if (($f fopen($file'rb')) ) {
  680.         if ($this->debugprint($this->debugend);
  681.         return PEAR::raiseError"Unable to open " $filePEAR_MP3_ID_FNO;
  682.     }
  683.  
  684.     $this->filesize = filesize($file);
  685.  
  686.     do {
  687.         while (fread($f,1!= Chr(255)) // Find the first frame
  688.         if ($this->debugecho "Find...\n";
  689.         if (feof($f)) {
  690.             if ($this->debugprint($this->debugend);
  691.             return PEAR::raiseError"No mpeg frame found"PEAR_MP3_ID_NOMP3;
  692.         }
  693.         }
  694.         fseek($fftell($f- 1)// back up one byte
  695.  
  696.         $frameoffset ftell($f);
  697.  
  698.         $r fread($f4);
  699.         // Binary to Hex to a binary sting. ugly but best I can think of.
  700.         // $bits = unpack('H*bits', $r);
  701.         // $bits =  base_convert($bits['bits'],16,2);
  702.         $bits sprintf("%'08b%'08b%'08b%'08b"ord($r{0})ord($r{1})ord($r{2})ord($r{3}));
  703.     while (!$bits[8and !$bits[9and !$bits[10])// 1st 8 bits true from the while
  704.     if ($this->debugprint('Bits: ' $bits "\n");
  705.  
  706.     $this->frameoffset = $frameoffset;
  707.  
  708.     // Detect VBR header
  709.     if ($bits[11== 0{
  710.         if (($bits[24== 1&& ($bits[25== 1)) {
  711.             $vbroffset = 9; // MPEG 2.5 Mono
  712.         else {
  713.             $vbroffset = 17; // MPEG 2.5 Stereo
  714.         }
  715.     else if ($bits[12== 0{
  716.         if (($bits[24== 1&& ($bits[25== 1)) {
  717.             $vbroffset = 9; // MPEG 2 Mono
  718.         else {
  719.             $vbroffset = 17; // MPEG 2 Stereo
  720.         }
  721.     else {
  722.         if (($bits[24== 1&& ($bits[25== 1)) {
  723.             $vbroffset = 17; // MPEG 1 Mono
  724.         else {
  725.             $vbroffset = 32; // MPEG 1 Stereo
  726.         }
  727.     }
  728.  
  729.     fseek($fftell($f$vbroffset);
  730.     $r fread($f4);
  731.  
  732.     switch ($r{
  733.         case 'Xing':
  734.             $this->encoding_type = 'VBR';
  735.         case 'Info':
  736.             // Extract info from Xing header
  737.  
  738.             if ($this->debugprint('Encoding Header: ' $r "\n");
  739.  
  740.             $r fread($f4);
  741.             $vbrbits sprintf("%'08b"ord($r{3}));
  742.  
  743.             if ($this->debugprint('XING Header Bits: ' $vbrbits "\n");
  744.  
  745.             if ($vbrbits[7== 1{
  746.                 // Next 4 bytes contain number of frames
  747.                 $r fread($f4);
  748.                 $this->frames = unpack('N'$r);
  749.                 $this->frames = $this->frames[1];
  750.             }
  751.  
  752.             if ($vbrbits[6== 1{
  753.                 // Next 4 bytes contain number of bytes
  754.                 $r fread($f4);
  755.                 $this->musicsize = unpack('N'$r);
  756.                 $this->musicsize = $this->musicsize[1];
  757.             }
  758.  
  759.             if ($vbrbits[5== 1{
  760.                 // Next 100 bytes contain TOC entries, skip
  761.                 fseek($fftell($f+ 100);
  762.             }
  763.  
  764.             if ($vbrbits[4== 1{
  765.                 // Next 4 bytes contain Quality Indicator
  766.                 $r fread($f4);
  767.                 $this->quality = unpack('N'$r);
  768.                 $this->quality = $this->quality[1];
  769.             }
  770.  
  771.             break;
  772.  
  773.         case 'VBRI':
  774.         default:
  775.             if ($vbroffset != 32{
  776.                 // VBRI Header is fixed after 32 bytes, so maybe we are looking at the wrong place.
  777.                 fseek($fftell($f+ 32 - $vbroffset);
  778.                 $r fread($f4);
  779.  
  780.                 if ($r != 'VBRI'{
  781.                     $this->encoding_type = 'CBR';
  782.                     break;
  783.                 }
  784.             else {
  785.                 $this->encoding_type = 'CBR';
  786.                 break;
  787.             }
  788.  
  789.             if ($this->debugprint('Encoding Header: ' $r "\n");
  790.  
  791.             $this->encoding_type = 'VBR';
  792.  
  793.             // Next 2 bytes contain Version ID, skip
  794.             fseek($fftell($f+ 2);
  795.  
  796.             // Next 2 bytes contain Delay, skip
  797.             fseek($fftell($f+ 2);
  798.  
  799.             // Next 2 bytes contain Quality Indicator
  800.             $r fread($f2);
  801.             $this->quality = unpack('n'$r);
  802.             $this->quality = $this->quality[1];
  803.  
  804.             // Next 4 bytes contain number of bytes
  805.             $r fread($f4);
  806.             $this->musicsize = unpack('N'$r);
  807.             $this->musicsize = $this->musicsize[1];
  808.  
  809.             // Next 4 bytes contain number of frames
  810.             $r fread($f4);
  811.             $this->frames = unpack('N'$r);
  812.             $this->frames = $this->frames[1];
  813.     }
  814.  
  815.     fclose($f);
  816.  
  817.     if ($bits[11== 0{
  818.         $this->mpeg_ver = "2.5";
  819.         $bitrates = array(
  820.             '1' => array(03248566480961121281441601761922242560),
  821.             '2' => array(0,  81624324048,  56,  64,  80,  961121281441600),
  822.             '3' => array(0,  81624324048,  56,  64,  80,  961121281441600),
  823.                  );
  824.     else if ($bits[12== 0{
  825.         $this->mpeg_ver = "2";
  826.         $bitrates = array(
  827.             '1' => array(03248566480961121281441601761922242560),
  828.             '2' => array(0,  81624324048,  56,  64,  80,  961121281441600),
  829.             '3' => array(0,  81624324048,  56,  64,  80,  961121281441600),
  830.                  );
  831.     else {
  832.         $this->mpeg_ver = "1";
  833.         $bitrates = array(
  834.             '1' => array(03264961281601922242562883203523844164480),
  835.             '2' => array(0324856,  64,  80,  961121281601922242563203840),
  836.             '3' => array(0324048,  56,  64,  80,  961121281601922242563200),
  837.                  );
  838.     }
  839.     if ($this->debugprint('MPEG' $this->mpeg_ver . "\n");
  840.  
  841.     $layer = array(
  842.         array(0,3),
  843.         array(2,1),
  844.               );
  845.     $this->layer = $layer[$bits[13]][$bits[14]];
  846.     if ($this->debugprint('layer: ' $this->layer . "\n");
  847.  
  848.     if ($bits[15== 0{
  849.         // It's backwards, if the bit is not set then it is protected.
  850.         if ($this->debugprint("protected (crc)\n");
  851.         $this->crc = true;
  852.     }
  853.  
  854.     $bitrate = 0;
  855.     if ($bits[16== 1$bitrate += 8;
  856.     if ($bits[17== 1$bitrate += 4;
  857.     if ($bits[18== 1$bitrate += 2;
  858.     if ($bits[19== 1$bitrate += 1;
  859.     $this->bitrate = $bitrates[$this->layer][$bitrate];
  860.  
  861.     $frequency = array(
  862.         '1' => array(
  863.             '0' => array(4410048000),
  864.             '1' => array(320000),
  865.                 ),
  866.         '2' => array(
  867.             '0' => array(2205024000),
  868.             '1' => array(160000),
  869.                 ),
  870.         '2.5' => array(
  871.             '0' => array(1102512000),
  872.             '1' => array(80000),
  873.                   ),
  874.           );
  875.     $this->frequency = $frequency[$this->mpeg_ver][$bits[20]][$bits[21]];
  876.  
  877.     $this->padding = $bits[22];
  878.     $this->private = $bits[23];
  879.  
  880.     $mode = array(
  881.         array('Stereo''Joint Stereo'),
  882.         array('Dual Channel''Mono'),
  883.              );
  884.     $this->mode = $mode[$bits[24]][$bits[25]];
  885.  
  886.     // XXX: I dunno what the mode extension is for bits 26,27
  887.  
  888.     $this->copyright = $bits[28];
  889.     $this->original = $bits[29];
  890.  
  891.     $emphasis = array(
  892.         array('none''50/15ms'),
  893.         array('''CCITT j.17'),
  894.              );
  895.     $this->emphasis = $emphasis[$bits[30]][$bits[31]];
  896.  
  897.     $samplesperframe = array(
  898.         '1' => array(
  899.             '1' => 384,
  900.             '2' => 1152,
  901.             '3' => 1152
  902.         ),
  903.         '2' => array(
  904.             '1' => 384,
  905.             '2' => 1152,
  906.             '3' => 576
  907.         ),
  908.         '2.5' => array(
  909.             '1' => 384,
  910.             '2' => 1152,
  911.             '3' => 576
  912.         ),
  913.     );
  914.     $this->samples_per_frame = $samplesperframe[$this->mpeg_ver][$this->layer];
  915.  
  916.     if ($this->encoding_type != 'VBR'{
  917.         if ($this->bitrate == 0{
  918.             $s = -1;
  919.         else {
  920.             $s ((8*filesize($this->file))/1000$this->bitrate;
  921.         }
  922.         $this->length = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
  923.         $this->lengthh = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
  924.         $this->lengths = (int)$s;
  925.  
  926.         $this->samples = ceil($this->lengths * $this->frequency);
  927.         if(0 != $this->samples_per_frame{
  928.             $this->frames = ceil($this->samples / $this->samples_per_frame);
  929.         else {
  930.             $this->frames = 0;
  931.         }
  932.         $this->musicsize = ceil($this->lengths * $this->bitrate * 1000 / 8);
  933.     else {
  934.         $this->samples = $this->samples_per_frame * $this->frames;
  935.         $s $this->samples / $this->frequency;
  936.  
  937.         $this->length = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
  938.         $this->lengthh = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
  939.         $this->lengths = (int)$s;
  940.  
  941.         $this->bitrate = (int)(($this->musicsize / $s* 8 / 1000);
  942.     }
  943.  
  944.     if ($this->debugprint($this->debugend);
  945.     // _readframe()
  946.  
  947.     /**
  948.      * getGenre - return the name of a genre number
  949.      *
  950.      * if no genre number is specified the genre number from
  951.      * $this->genreno will be used.
  952.      *
  953.      * the genre is returned or false if an error or not found
  954.      * no error message is ever returned
  955.      *
  956.      * @param   integer $genreno Number of the genre
  957.      * @return  mixed   false, if no genre found, else string
  958.      *
  959.      * @access public
  960.      */
  961.     function getGenre($genreno{
  962.     if ($this->debugprint($this->debugbeg . "getgenre($genreno)<HR>\n");
  963.  
  964.     $genres $this->genres();
  965.     if (isset($genres[$genreno])) {
  966.         $genre $genres[$genreno];
  967.         if ($this->debugprint($genre "\n");
  968.     else {
  969.         $genre '';
  970.     }
  971.  
  972.     if ($this->debugprint($this->debugend);
  973.     return $genre;
  974.     // getGenre($genreno)
  975.  
  976.     /*
  977.      * getGenreNo - return the number of the genre name
  978.      *
  979.      * the genre number is returned or 0xff (255) if a match is not found
  980.      * you can specify the default genreno to use if one is not found
  981.      * no error message is ever returned
  982.      *
  983.      * @param   string  $genre      Name of the genre
  984.      * @param   integer $default    Genre number in case of genre not found
  985.      *
  986.      * @access public
  987.      */
  988.     function getGenreNo($genre$default = 0xff{
  989.     if ($this->debugprint($this->debugbeg . "getgenreno('$genre',$default)<HR>\n");
  990.  
  991.     $genres $this->genres();
  992.     $genreno = false;
  993.     if ($genre{
  994.         foreach ($genres as $no => $name{
  995.         if (strtolower($genre== strtolower($name)) {
  996.             if ($this->debugprint("$no:'$name' == '$genre'");
  997.             $genreno $no;
  998.         }
  999.         }
  1000.     }
  1001.     if ($genreno === false$genreno $default;
  1002.     if ($this->debugprint($this->debugend);
  1003.     return $genreno;
  1004.     // getGenreNo($genre, $default = 0xff)
  1005.  
  1006.     /*
  1007.      * genres - returns an array of the ID3v1 genres
  1008.      *
  1009.      * @return array
  1010.      *
  1011.      * @access public
  1012.      */
  1013.     function genres({
  1014.     return array(
  1015.         0   => 'Blues',
  1016.         1   => 'Classic Rock',
  1017.         2   => 'Country',
  1018.         3   => 'Dance',
  1019.         4   => 'Disco',
  1020.         5   => 'Funk',
  1021.         6   => 'Grunge',
  1022.         7   => 'Hip-Hop',
  1023.         8   => 'Jazz',
  1024.         9   => 'Metal',
  1025.         10  => 'New Age',
  1026.         11  => 'Oldies',
  1027.         12  => 'Other',
  1028.         13  => 'Pop',
  1029.         14  => 'R&B',
  1030.         15  => 'Rap',
  1031.         16  => 'Reggae',
  1032.         17  => 'Rock',
  1033.         18  => 'Techno',
  1034.         19  => 'Industrial',
  1035.         20  => 'Alternative',
  1036.         21  => 'Ska',
  1037.         22  => 'Death Metal',
  1038.         23  => 'Pranks',
  1039.         24  => 'Soundtrack',
  1040.         25  => 'Euro-Techno',
  1041.         26  => 'Ambient',
  1042.         27  => 'Trip-Hop',
  1043.         28  => 'Vocal',
  1044.         29  => 'Jazz+Funk',
  1045.         30  => 'Fusion',
  1046.         31  => 'Trance',
  1047.         32  => 'Classical',
  1048.         33  => 'Instrumental',
  1049.         34  => 'Acid',
  1050.         35  => 'House',
  1051.         36  => 'Game',
  1052.         37  => 'Sound Clip',
  1053.         38  => 'Gospel',
  1054.         39  => 'Noise',
  1055.         40  => 'Alternative Rock',
  1056.         41  => 'Bass',
  1057.         42  => 'Soul',
  1058.         43  => 'Punk',
  1059.         44  => 'Space',
  1060.         45  => 'Meditative',
  1061.         46  => 'Instrumental Pop',
  1062.         47  => 'Instrumental Rock',
  1063.         48  => 'Ethnic',
  1064.         49  => 'Gothic',
  1065.         50  => 'Darkwave',
  1066.         51  => 'Techno-Industrial',
  1067.         52  => 'Electronic',
  1068.         53  => 'Pop-Folk',
  1069.         54  => 'Eurodance',
  1070.         55  => 'Dream',
  1071.         56  => 'Southern Rock',
  1072.         57  => 'Comedy',
  1073.         58  => 'Cult',
  1074.         59  => 'Gangsta',
  1075.         60  => 'Top 40',
  1076.         61  => 'Christian Rap',
  1077.         62  => 'Pop/Funk',
  1078.         63  => 'Jungle',
  1079.         64  => 'Native US',
  1080.         65  => 'Cabaret',
  1081.         66  => 'New Wave',
  1082.         67  => 'Psychadelic',
  1083.         68  => 'Rave',
  1084.         69  => 'Showtunes',
  1085.         70  => 'Trailer',
  1086.         71  => 'Lo-Fi',
  1087.         72  => 'Tribal',
  1088.         73  => 'Acid Punk',
  1089.         74  => 'Acid Jazz',
  1090.         75  => 'Polka',
  1091.         76  => 'Retro',
  1092.         77  => 'Musical',
  1093.         78  => 'Rock & Roll',
  1094.         79  => 'Hard Rock',
  1095.         80  => 'Folk',
  1096.         81  => 'Folk-Rock',
  1097.         82  => 'National Folk',
  1098.         83  => 'Swing',
  1099.         84  => 'Fast Fusion',
  1100.         85  => 'Bebob',
  1101.         86  => 'Latin',
  1102.         87  => 'Revival',
  1103.         88  => 'Celtic',
  1104.         89  => 'Bluegrass',
  1105.         90  => 'Avantgarde',
  1106.         91  => 'Gothic Rock',
  1107.         92  => 'Progressive Rock',
  1108.         93  => 'Psychedelic Rock',
  1109.         94  => 'Symphonic Rock',
  1110.         95  => 'Slow Rock',
  1111.         96  => 'Big Band',
  1112.         97  => 'Chorus',
  1113.         98  => 'Easy Listening',
  1114.         99  => 'Acoustic',
  1115.         100 => 'Humour',
  1116.         101 => 'Speech',
  1117.         102 => 'Chanson',
  1118.         103 => 'Opera',
  1119.         104 => 'Chamber Music',
  1120.         105 => 'Sonata',
  1121.         106 => 'Symphony',
  1122.         107 => 'Booty Bass',
  1123.         108 => 'Primus',
  1124.         109 => 'Porn Groove',
  1125.         110 => 'Satire',
  1126.         111 => 'Slow Jam',
  1127.         112 => 'Club',
  1128.         113 => 'Tango',
  1129.         114 => 'Samba',
  1130.         115 => 'Folklore',
  1131.         116 => 'Ballad',
  1132.         117 => 'Power Ballad',
  1133.         118 => 'Rhytmic Soul',
  1134.         119 => 'Freestyle',
  1135.         120 => 'Duet',
  1136.         121 => 'Punk Rock',
  1137.         122 => 'Drum Solo',
  1138.         123 => 'Acapella',
  1139.         124 => 'Euro-House',
  1140.         125 => 'Dance Hall',
  1141.         126 => 'Goa',
  1142.         127 => 'Drum & Bass',
  1143.         128 => 'Club-House',
  1144.         129 => 'Hardcore',
  1145.         130 => 'Terror',
  1146.         131 => 'Indie',
  1147.         132 => 'BritPop',
  1148.         133 => 'Negerpunk',
  1149.         134 => 'Polsk Punk',
  1150.         135 => 'Beat',
  1151.         136 => 'Christian Gangsta Rap',
  1152.         137 => 'Heavy Metal',
  1153.         138 => 'Black Metal',
  1154.         139 => 'Crossover',
  1155.         140 => 'Contemporary Christian',
  1156.         141 => 'Christian Rock',
  1157.         142 => 'Merengue',
  1158.         143 => 'Salsa',
  1159.         144 => 'Trash Metal',
  1160.         145 => 'Anime',
  1161.         146 => 'Jpop',
  1162.         147 => 'Synthpop'
  1163.             );
  1164.     // genres
  1165. // end of id3
  1166.  
  1167. ?>

Documentation generated on Mon, 11 Mar 2019 14:05:48 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.