Source Rally PHP Community Scripts .. Sign up .. Login
A class with some stuff implented to mess around with MP3-files.
Please note:
- This script has at least one bug - it won't be able to tell the length of all MP3s.
- It is very slow an takes up a lot of memory.
- It will only work with PHP5+ (small modifications will make it work in PHP4 as well)
- Still looking to identify and resolve more issues. Please submit any bugs found.
Thank you to Andres at fuck finder app and Elan at alt dating for identifying MP3 with image file bug.

LGPL License
Access: Public      Tags: PHP, MP3, IDV2
Add to favourites       Subscribe comments       Copy code       Bookmark
<?php
/*
//Merge two files
$path = 'path.mp3';
$path1 = 'path1.mp3';
$mp3 = new mp3($path);

$newpath = 'path.mp3';
$mp3->striptags();

$mp3_1 = new mp3($path1);
$mp3->mergeBehind($mp3_1);
$mp3->striptags();
$mp3->setIdv3_2('01','Track Title','Artist','Album','Year','Genre','Comments','Composer','OrigArtist',
'Copyright','url','encodedBy');
$mp3->save($newpath);


//Extract 30 seconds starting after 10 seconds.
$path = 'path.mp3';
$mp3 = new mp3($path);
$mp3_1 = $mp3->extract(10,30);
$mp3_1->save('newpath.mp3');

//Extract the exact length of time
$path = 'path.mp3';
$mp3 = new mp3($path);
$mp3->setFileInfoExact();
echo $mp3->time;
//note that this is the exact length!
*/
class mp3
{
    var 
$str;
    var 
$time;
    var 
$frames;
    
    function 
mp3($path="")
    {
        if(
$path!="")
        {
            
$this->str file_get_contents($path);
        }
    }
    
    function 
setStr($str)
    {
        
$this->str $str;
    }
    
    function 
setFileInfoExact()
    {
        
$maxStrLen strlen($this->str);
        
$currentStrPos strpos($this->str,chr(255));
        
$framesCount=0;
        
$time 0;
        while(
$currentStrPos $maxStrLen)
        {
            
$str substr($this->str,$currentStrPos,4);
            
$strlen strlen($str);
            
$parts = array();
            for(
$i=0;$i $strlen;$i++)
            {
                
$parts[] = $this->decbinFill(ord($str[$i]),8);
            }
            if(
$parts[0] != "11111111")
            {
                if((
$maxStrLen-128) > $currentStrPos)
                {
                    return 
false;
                }
                else
                {
                    
$this->time $time;
                    
$this->frames $framesCount;
                    return 
true;
                }
            }
            
$a $this->doFrameStuff($parts);
            
$currentStrPos += $a[0];
            
$time += $a[1];
            
$framesCount++;
        }
        
$this->time $time;
        
$this->frames $framesCount;
        return 
true;
    }
    
    function 
extract($start,$length)
    {
        
$maxStrLen strlen($this->str);
        
$currentStrPos strpos($this->str,chr(255));
        
$framesCount=0;
        
$time 0;
        
$startCount = -1;
        
$endCount = -1;
        while(
$currentStrPos $maxStrLen)
        {
            if(
$startCount==-1&&$time>=$start)
            {
                
$startCount $currentStrPos;
            }
            if(
$endCount==-1&&$time>=($start+$length))
            {
                
$endCount $currentStrPos-$startCount;
            }
            
$doFrame true;
            
$str substr($this->str,$currentStrPos,4);
            
$strlen strlen($str);
            
$parts = array();
            for(
$i=0;$i $strlen;$i++)
            {
                
$parts[] = $this->decbinFill(ord($str[$i]),8);
            }
            if(
$parts[0] != "11111111")
            {
                if((
$maxStrLen-128) > $currentStrPos)
                {
                    
$doFrame false;
                }
                else
                {
                    
$doFrame false;
                }
            }
            if(
$doFrame)
            {
                
$a $this->doFrameStuff($parts);
                
$currentStrPos += $a[0];
                
$time += $a[1];
                
$framesCount++;
            }
        }
        
$mp3 = new mp3();
        if(
$endCount == -1)
        {
            
$endCount $maxStrLen-$startCount;
        }
        if(
$startCount!=-1&&$endCount!=-1)
        {
            
$mp3->setStr(substr($this->str,$startCount,$endCount));
        }
        return 
$mp3;
    }
    
    function 
decbinFill($dec,$length=0)
    {
        
$str decbin($dec);
        
$nulls $length-strlen($str);
        if(
$nulls>0)
        {
            for(
$i=0;$i<$nulls;$i++)
            {
                
$str '0'.$str;
            }
        }
        return 
$str;
    }
    
    function 
doFrameStuff($parts)
    {
        
//Get Audio Version
        
$errors = array();
        switch(
substr($parts[1],3,2))
        {
            case 
'01':
            
$errors[]='Reserved audio version';
            break;
            case 
'00':
            
$audio 2.5;
            break;
            case 
'10':
            
$audio 2;
            break;
            case 
'11':
            
$audio 1;
            break;
        }
        
//Get Layer
        
switch(substr($parts[1],5,2))
        {
            case 
'01':
            
$layer 3;
            break;
            case 
'00':
            
$errors[]='Reserved layer';
            break;
            case 
'10':
            
$layer 2;
            break;
            case 
'11':
            
$layer 1;
            break;
        }
        
//Get Bitrate
        
$bitFlag substr($parts[2],0,4);
        
$bitArray = array(
    
'0000'    => array(free,    free,    free,    free,    free),
    
'0001'    => array(32,    32,    32,    32,    8),
    
'0010'    => array(64,    48,    40,    48,    16),
    
'0011'    => array(96,    56,    48,    56,    24),
    
'0100'    => array(128,    64,    56,    64,    32),
    
'0101'    => array(160,    80,    64,    80,    40),
    
'0110'    => array(192,    96,    80,    96,    48),
    
'0111'    => array(224,    112,    96,    112,    56),
    
'1000'    => array(256,    128,    112,    128,    64),
    
'1001'    => array(288,    160,    128,    144,    80),
    
'1010'    => array(320,    192,    160,    160,    96),
    
'1011'    => array(352,    224,    192,    176,    112),
    
'1100'    => array(384,    256,    224,    192,    128),
    
'1101'    => array(416,    320,    256,    224,    144),
    
'1110'    => array(448,    384,    320,    256,    160),
    
'1111'    => array(bad,    bad,    bad,    bad,    bad)
    );
        
$bitPart $bitArray[$bitFlag];
        
$bitArrayNumber;
        if(
$audio==1)
        {
            switch(
$layer)
            {
                case 
1:
                
$bitArrayNumber=0;
                break;
                case 
2:
                
$bitArrayNumber=1;
                break;
                case 
3:
                
$bitArrayNumber=2;
                break;
            }
        }
        else
        {
            switch(
$layer)
            {
                case 
1:
                
$bitArrayNumber=3;
                break;
                case 
2:
                
$bitArrayNumber=4;
                break;
                case 
3:
                
$bitArrayNumber=4;
                break;
            }
        }
        
$bitRate $bitPart[$bitArrayNumber];
        
//Get Frequency
        
$frequencies = array(
        
1=>array('00'=>44100,
        
'01'=>48000,
        
'10'=>32000,
        
'11'=>'reserved'),
        
2=>array(),
        
2.5=>array());
        
$freq $frequencies[$audio][substr($parts[2],4,2)];
        
//IsPadded?
        
$padding substr($parts[2],6,1);
        if(
$layer==3||$layer==2)
        {
            
//FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
            
$frameLength 144 $bitRate 1000 $freq $padding;
        }
        
$frameLength floor($frameLength);
        
$seconds += $frameLength*8/($bitRate*1000);
        return array(
$frameLength,$seconds);
        
//Calculate next when next frame starts.
        //Capture next frame.    
    
}
    
    function 
setIdv3_2($track,$title,$artist,$album,$year,$genre,$comments,$composer,$origArtist,
$copyright,$url,$encodedBy)
    {
        
$urlLength = (int)(strlen($url)+2);
        
$copyrightLength = (int)(strlen($copyright)+1);
        
$origArtistLength = (int)(strlen($origArtist)+1);
        
$composerLength = (int)(strlen($composer)+1);
        
$commentsLength = (int)strlen($comments)+5;
        
$titleLength = (int) strlen($title)+1;
        
$artistLength = (int)strlen($artist)+1;
        
$albumLength = (int) strlen($album)+1;
        
$genreLength = (int) strlen($genre)+1;
        
$encodedByLength = (int)(strlen($encodedBy)+1);
        
$trackLength = (int) strlen($track) + 1;
        
$yearLength = (int) strlen($year)+1;
        
$str .= chr(73);//I
        
$str .= chr(68);//D
        
$str .= chr(51);//3
        
$str .= chr(3);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(8);//
        
$str .= chr(53);//5
        
$str .= chr(84);//T
        
$str .= chr(82);//R
        
$str .= chr(67);//C
        
$str .= chr(75);//K
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($trackLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $track;
        
$str .= chr(84);//T
        
$str .= chr(69);//E
        
$str .= chr(78);//N
        
$str .= chr(67);//C
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($encodedByLength);//
        
$str .= chr(64);//@
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $encodedBy;
        
$str .= chr(87);//W
        
$str .= chr(88);//X
        
$str .= chr(88);//X
        
$str .= chr(88);//X
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($urlLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $url;
        
$str .= chr(84);//T
        
$str .= chr(67);//C
        
$str .= chr(79);//O
        
$str .= chr(80);//P
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($copyrightLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $copyright;
        
$str .= chr(84);//T
        
$str .= chr(79);//O
        
$str .= chr(80);//P
        
$str .= chr(69);//E
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($origArtistLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $origArtist;
        
$str .= chr(84);//T
        
$str .= chr(67);//C
        
$str .= chr(79);//O
        
$str .= chr(77);//M
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($composerLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $composer;
        
$str .= chr(67);//C
        
$str .= chr(79);//O
        
$str .= chr(77);//M
        
$str .= chr(77);//M
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($commentsLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(9);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $comments;
        
$str .= chr(84);//T

        
$str .= chr(67);//C
        
$str .= chr(79);//O
        
$str .= chr(78);//N
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($genreLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $genre;
        
$str .= chr(84);//T
        
$str .= chr(89);//Y
        
$str .= chr(69);//E
        
$str .= chr(82);//R
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($yearLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $year;
        
$str .= chr(84);//T
        
$str .= chr(65);//A
        
$str .= chr(76);//L
        
$str .= chr(66);//B
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($albumLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $album;
        
$str .= chr(84);//T
        
$str .= chr(80);//P
        
$str .= chr(69);//E
        
$str .= chr(49);//1
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($artistLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $artist;
        
$str .= chr(84);//T
        
$str .= chr(73);//I
        
$str .= chr(84);//T
        
$str .= chr(50);//2
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr($titleLength);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= chr(0);//
        
$str .= $title;
        
$this->str $str.$this->str;
    }
    
    function 
mergeBehind(mp3 $mp3)
    {
        
$this->str .= $mp3->str;
    }
    
    function 
mergeInfront(mp3 $mp3)
    {
        
$this->str $mp3->str.$this->str;
    }
    
    function 
getIdvEnd()
    {
        
$strlen strlen($this->str);
        
$str substr($this->str,($strlen-128));
        
$str1 substr($str,0,3);
        if(
strtolower($str1) == strtolower('TAG'))
        {
            return 
$str;
        }
        else
        {
            return 
false;
        }
    }
    
    function 
getStart()
    {
        
$strlen strlen($this->str);
        for(
$i=0;$i<$strlen;$i++)
        {
            
$v substr($this->str,$i,1);
            
$value ord($v);
            if(
$value == 255)
            {
                return 
$i;
            }
        }
    }
    
    function 
striptags()
    {
        
//Remove start stuff...
        
$newStr '';
        
$s $start $this->getStart();
        if(
$s===false)
        {
            return 
false;
        }
        else
        {
            
$this->str substr($this->str,$start);
        }
        
//Remove end tag stuff
        
$end $this->getIdvEnd();
        if(
$end!==false)
        {
            
$this->str substr($this->str,0,(strlen($this->str)-129));
        }
    }
    
    function 
save($path)
    {
        
$fp fopen($path,'w');
        
fwrite($fp,$this->str);
        
fclose($fp);
    }


//join various MP3s
    
function multiJoin($newpath,$array)
    {
        foreach (
$array as $path)
        {
            
$mp3 = new mp3($path);
            
$mp3->striptags();
            
$mp3_1 = new mp3($newpath);
            
$mp3->mergeBehind($mp3_1);
            
$mp3->save($newpath);
        }
    }
}
?>
Add to favourites       Subscribe comments       Copy code       Bookmark
Sign up to add your own comment here!

Comments

SinneR_CRO - 26-04-2011 14:32
Add to friends       Mail user
Hi Regin.
I have a problem with extracting parts of mp3 file; it won't work for me.
Do you have any suggestions about it? I am trying to create a site for adult service providers similar to skip da games. Any tips for this endeavor would also be appreciated.

Thanx,
Siniša
PHPEcono - 29-01-2011 18:21
Add to friends       Mail user
Hi regin first of all thx for this wanderful class! I have been using it for the past 2 years on one of my clients project. It has been very handy ;-) I was wandering if theres a way to create an audio fadein fadeout on X number of secondes when an mp3 is created.
cube660 - 05-09-2010 20:32
Add to friends       Mail user
Does anyone have a valid link to an updated and corrected version of this code? Would you please provide it here, or perhaps even post the new code.

Thanks
taziyounes - 19-07-2010 18:29
Add to friends       Mail user
Hi, ì try to add some content like a Title in arabic but it doesn't work, How can we use the UTF-8 encoding.
Thanks
avardar - 08-06-2010 20:31
Add to friends       Mail user
it gives "PHP Warning: Division by zero in" error if mp3 file has image inside ???
regin - 17-02-2010 15:44
Add to friends       Mail user
I just added the method multiJoin() - Manuel Lama ( http://www.sourcerally.net/Manuellama ) forwarded me the sourcecode...

Please, remember that I don't recommend this sourcecode for production purposes...
luciferus - 06-03-2009 10:10
Add to friends       Mail user
Hi. I have tried to bound several mp3 files. If the files have different bitrate the final mp3 file will not sound good at all.
jirka - 26-01-2009 14:54
Add to friends       Mail user
Hi,
I need to get smaller file(mp3) from this class. Is here any option? (some compression)

Jirka
kamilmajewski - 26-12-2008 12:46
Add to friends       Mail user
corrected code HERE: http://www.agencjawebowa.com/klienci/mp3/mp3.zip
kamilmajewski - 26-12-2008 12:45
Add to friends       Mail user
I found out another problem with $frequencies (line 282). Some files got mark for 2, 2.5 and due an empty array we got division by zero (in lines 291).

A little MOD:
1=>array('00'=>44100,
'01'=>48000,
'10'=>32000,
'11'=>'reserved'),
2=>array('00'=>44100,
'01'=>48000,
'10'=>32000,
'11'=>'reserved'),
2.5=>array('00'=>44100,
'01'=>48000,
'10'=>32000,
'11'=>'reserved'));
Grr1967 - 19-08-2008 19:53
Add to friends       Mail user
To BJMg:
Take a look at the post I loaded on 02-04-2008.
regin - 19-08-2008 19:53
Add to friends       Mail user
sorry can't help.
BJMg - 19-08-2008 18:13
Add to friends       Mail user
hi, thank you for this script, but I have a problem.
When I try to use your class with some mp3 files, so it wrote me "Fatal error:Maximum execution time of 30 seconds exceeded in...".
Can you help me please?


sorry my english isn´t excelent.
hany - 09-07-2008 20:42
Add to friends       Mail user
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 29148598 bytes)
regin - 17-06-2008 19:46
Add to friends       Mail user
It only supports the functions listed.
nr23457103 - 17-06-2008 19:38
Add to friends       Mail user
That is exactly what is was searching for since months!

Question: Is There also a posibillity to set the BitRate of the extracted Sample file?

Thanks!
Grr1967 - 02-04-2008 08:47
Add to friends       Mail user
[this is a follow-up from my previous comment]

Regarding division by zero and timeouts, I believe I cracked the issue.

The script is trying to locate the MP3 sync word, by running the following command:
$currentStrPos = strpos($this->str, chr(255));
This command is looking for a secuence of 0xFF in the file content. However, there are cases that the IDv3Tags that preceed the MP3 content also contains 0xFF, and so the currentStrPos points to the wrong offset in the string.

In order to fix it, I changed the line
$currentStrPos = strpos($this->str, chr(255));
to
$currentStrPos = $this->getStart();
And revised the getStart to look for the correct syncWord by looking at the frame (using doFrame) and see if there is a sensible content in the frame.

Note that there are two locations in the code that needs to be replaced, one in extract(...) and the other in setFileInfoExact(...)

Another issue that I found is in extract. The code sets a variable $doFrame to either true or false. If it's true - it calls to doFrameStuff, otherwise - it does not. However, in the case that it does not call to doFrameStuff - currentStrPos remains on the same location in the string, and so the while(...) loop start running endlessly, causing a timeout. To resolve the issue I changed the code as follows:
if($doFrame)
{
...same code as in the original script...
}
else
break;
The "else -> break" forces the script to exit the loop.

The revised code can be found in:
http://www.ClickFM.co.il/temp/ofer/mp3.txt
Change it to ".php" on your server.

Great great great script! I hope those fixes will make it bullet proof.
madlou - 28-01-2008 01:55
Add to friends       Mail user
Love the script, however I too get the 'divide by 0' error and page timeouts on certain mp3's. Unfortunatly by coding skills don't touch yours, so am a little lost now. Are you ever going to revisit this in the near future? If so, i'd be well chuffed!!
pkthree - 13-08-2007 00:56
Add to friends       Mail user
Thanks for the code. I've tweaked it slightly and implemented it into an anti-spam plugin for WordPress blogs: http://www.theblog.ca/?p=143
supershwa - 17-07-2007 01:09
Add to friends       Mail user
Nice script; having a hard time finding something that can MIX two files into one (not append one file to the end of the other.) Is this even possible via PHP without sox/soxmix or cutmp3?
vinayak - 14-06-2007 12:57
Add to friends       Mail user
$parts[0] is always 1111111 i think that is reason divison by zero error!
I have php5.1 still having problem plz tell me solution
mike - 06-06-2007 14:16
Add to friends       Mail user
Hi I'm getting a divide by 0 warning if I try to trim a mp3. I'm using php 5.2.1. I could not find a solution to this problem in this list.Could someone provide help ? This script would be really useful for me. Nice work!
SlicDiggy - 25-05-2007 01:12
Add to friends       Mail user
Er, "a" correct answer. There may be other ways.

And just to re-iterate, the "Divide by Zero" issue seems to be a pretty huge problem, and causes problems for 85% of the mp3s i've tried...which needless to say, can be a bit frustrating...

Has anyone found a solution to this problem yet?
SlicDiggy - 25-05-2007 01:09
Add to friends       Mail user
@nelwa - Dividing by 60 seems to do the trick at first glance for some mp3s, however it doesn't resolve most of the time, and doesn't seem to be the correct answer. In fact, I've listed the correct answer below:

echo floor($mp3->time/60).':'.sprintf("%02d", round($mp3->time%60));
regin - 24-05-2007 19:30
Add to friends       Mail user
Sorry this script only work on PHP5.
nelwa - 24-05-2007 18:34
Add to friends       Mail user
@SlicDiggy

Just divide your total seconds by 60 to get to minutes...
nelwa - 24-05-2007 18:32
Add to friends       Mail user
Please help, I'm also getting the divide by zero error, using php 4.4.4.

Is this script php 4 compatible...

Thanks
snoopy86 - 24-05-2007 12:27
Add to friends       Mail user
Help I'm getting a divide by 0 warning too. I'm using php 5.2.1
I tried it out the other day and it worked perfectly fine but now it won't work when i trim a mp3. Please help...
SlicDiggy - 06-05-2007 04:38
Add to friends       Mail user
Nice class :) Love it. Two things though:

1) I'm not great with math, and I'm having trouble converting the Exact Time into Minutes. Can you help me out?

2) For some reason any time I try to use the functions that modify mp3s, i get a php time-out after 60 seconds. Any ideas why?
regin - 13-03-2007 22:39
Add to friends       Mail user
Notes to myself:
1. Support "correct fixed bitrate": Extracting + calculating.
2. Exception on merging files that have "correct fixed bitrate" frames.
More comments: 1 2 Next
Shared by:

regin

Mail user Add to friends
All user contributed content is available under the unless specified otherwise.
Remaining copyrights Regin Gaarsmand © 2006-2008
About www.SourceRally.net