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
<?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);
}
}
} ?>
Sign up to add your own comment here!
Comments
|
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 |
|
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. |
|
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 |
|
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 |
|
it gives "PHP Warning: Division by zero in" error if mp3 file has image inside ??? |
|
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... |
|
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. |
|
Hi, I need to get smaller file(mp3) from this class. Is here any option? (some compression)
Jirka |
|
corrected code HERE: http://www.agencjawebowa.com/klienci/mp3/mp3.zip |
|
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')); |
|
To BJMg: Take a look at the post I loaded on 02-04-2008. |
|
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. |
|
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 29148598 bytes) |
|
It only supports the functions listed. |
|
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! |
|
[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. |
|
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!! |
|
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 |
|
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? |
|
$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 |
|
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! |
|
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? |
|
@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)); |
|
Sorry this script only work on PHP5. |
|
@SlicDiggy
Just divide your total seconds by 60 to get to minutes... |
|
Please help, I'm also getting the divide by zero error, using php 4.4.4.
Is this script php 4 compatible...
Thanks |
|
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... |
|
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? |
|
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 |
|