How do I find out the length of an audio through PHP?

2

I have a system where the user uploads an audio file. I need to save in the database some information from this audio file, such as the file size on disk and the duration.

These files can be in a variety of audio formats.

I already know that I can capture audio size information on disk through the filesize function.

But now I need to capture information on the duration.

How can I do this in PHP?

Example:

$arquivo = Input::file('arquivo');

$filename = $arquivo->getRealPath();

$info= [
    'tamanho' => $arquivo->getSize(),
    'duracao' => ???
]
    
asked by anonymous 22.08.2016 / 14:56

3 answers

1

To calculate the audio of an MP3, create a class mp3file.class.php with the following code:

<?php
class MP3File
{
    protected $filename;
    public function __construct($filename)
    {
        $this->filename = $filename;
    }

    public static function formatTime($duration) //as hh:mm:ss
    {
        //return sprintf("%d:%02d", $duration/60, $duration%60);
        $hours = floor($duration / 3600);
        $minutes = floor( ($duration - ($hours * 3600)) / 60);
        $seconds = $duration - ($hours * 3600) - ($minutes * 60);
        return sprintf("%02d:%02d:%02d", $hours, $minutes, $seconds);
    }

    //Read first mp3 frame only...  use for CBR constant bit rate MP3s
    public function getDurationEstimate()
    {
        return $this->getDuration($use_cbr_estimate=true);
    }

    //Read entire file, frame by frame... ie: Variable Bit Rate (VBR)
    public function getDuration($use_cbr_estimate=false)
    {
        $fd = fopen($this->filename, "rb");

        $duration=0;
        $block = fread($fd, 100);
        $offset = $this->skipID3v2Tag($block);
        fseek($fd, $offset, SEEK_SET);
        while (!feof($fd))
        {
            $block = fread($fd, 10);
            if (strlen($block)<10) { break; }
            //looking for 1111 1111 111 (frame synchronization bits)
            else if ($block[0]=="\xff" && (ord($block[1])&0xe0) )
            {
                $info = self::parseFrameHeader(substr($block, 0, 4));
                if (empty($info['Framesize'])) { return $duration; } //some corrupt mp3 files
                fseek($fd, $info['Framesize']-10, SEEK_CUR);
                $duration += ( $info['Samples'] / $info['Sampling Rate'] );
            }
            else if (substr($block, 0, 3)=='TAG')
            {
                fseek($fd, 128-10, SEEK_CUR);//skip over id3v1 tag size
            }
            else
            {
                fseek($fd, -9, SEEK_CUR);
            }
            if ($use_cbr_estimate && !empty($info))
            { 
                return $this->estimateDuration($info['Bitrate'],$offset); 
            }
        }
        return round($duration);
    }

    private function estimateDuration($bitrate,$offset)
    {
        $kbps = ($bitrate*1000)/8;
        $datasize = filesize($this->filename) - $offset;
        return round($datasize / $kbps);
    }

    private function skipID3v2Tag(&$block)
    {
        if (substr($block, 0,3)=="ID3")
        {
            $id3v2_major_version = ord($block[3]);
            $id3v2_minor_version = ord($block[4]);
            $id3v2_flags = ord($block[5]);
            $flag_unsynchronisation  = $id3v2_flags & 0x80 ? 1 : 0;
            $flag_extended_header    = $id3v2_flags & 0x40 ? 1 : 0;
            $flag_experimental_ind   = $id3v2_flags & 0x20 ? 1 : 0;
            $flag_footer_present     = $id3v2_flags & 0x10 ? 1 : 0;
            $z0 = ord($block[6]);
            $z1 = ord($block[7]);
            $z2 = ord($block[8]);
            $z3 = ord($block[9]);
            if ( (($z0&0x80)==0) && (($z1&0x80)==0) && (($z2&0x80)==0) && (($z3&0x80)==0) )
            {
                $header_size = 10;
                $tag_size = (($z0&0x7f) * 2097152) + (($z1&0x7f) * 16384) + (($z2&0x7f) * 128) + ($z3&0x7f);
                $footer_size = $flag_footer_present ? 10 : 0;
                return $header_size + $tag_size + $footer_size;//bytes to skip
            }
        }
        return 0;
    }

    public static function parseFrameHeader($fourbytes)
    {
        static $versions = array(
            0x0=>'2.5',0x1=>'x',0x2=>'2',0x3=>'1', // x=>'reserved'
        );
        static $layers = array(
            0x0=>'x',0x1=>'3',0x2=>'2',0x3=>'1', // x=>'reserved'
        );
        static $bitrates = array(
            'V1L1'=>array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448),
            'V1L2'=>array(0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384),
            'V1L3'=>array(0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320),
            'V2L1'=>array(0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256),
            'V2L2'=>array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160),
            'V2L3'=>array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160),
        );
        static $sample_rates = array(
            '1'   => array(44100,48000,32000),
            '2'   => array(22050,24000,16000),
            '2.5' => array(11025,12000, 8000),
        );
        static $samples = array(
            1 => array( 1 => 384, 2 =>1152, 3 =>1152, ), //MPEGv1,     Layers 1,2,3
            2 => array( 1 => 384, 2 =>1152, 3 => 576, ), //MPEGv2/2.5, Layers 1,2,3
        );
        //$b0=ord($fourbytes[0]);//will always be 0xff
        $b1=ord($fourbytes[1]);
        $b2=ord($fourbytes[2]);
        $b3=ord($fourbytes[3]);

        $version_bits = ($b1 & 0x18) >> 3;
        $version = $versions[$version_bits];
        $simple_version =  ($version=='2.5' ? 2 : $version);

        $layer_bits = ($b1 & 0x06) >> 1;
        $layer = $layers[$layer_bits];

        $protection_bit = ($b1 & 0x01);
        $bitrate_key = sprintf('V%dL%d', $simple_version , $layer);
        $bitrate_idx = ($b2 & 0xf0) >> 4;
        $bitrate = isset($bitrates[$bitrate_key][$bitrate_idx]) ? $bitrates[$bitrate_key][$bitrate_idx] : 0;

        $sample_rate_idx = ($b2 & 0x0c) >> 2;//0xc => b1100
        $sample_rate = isset($sample_rates[$version][$sample_rate_idx]) ? $sample_rates[$version][$sample_rate_idx] : 0;
        $padding_bit = ($b2 & 0x02) >> 1;
        $private_bit = ($b2 & 0x01);
        $channel_mode_bits = ($b3 & 0xc0) >> 6;
        $mode_extension_bits = ($b3 & 0x30) >> 4;
        $copyright_bit = ($b3 & 0x08) >> 3;
        $original_bit = ($b3 & 0x04) >> 2;
        $emphasis = ($b3 & 0x03);

        $info = array();
        $info['Version'] = $version;//MPEGVersion
        $info['Layer'] = $layer;
        //$info['Protection Bit'] = $protection_bit; //0=> protected by 2 byte CRC, 1=>not protected
        $info['Bitrate'] = $bitrate;
        $info['Sampling Rate'] = $sample_rate;
        //$info['Padding Bit'] = $padding_bit;
        //$info['Private Bit'] = $private_bit;
        //$info['Channel Mode'] = $channel_mode_bits;
        //$info['Mode Extension'] = $mode_extension_bits;
        //$info['Copyright'] = $copyright_bit;
        //$info['Original'] = $original_bit;
        //$info['Emphasis'] = $emphasis;
        $info['Framesize'] = self::framesize($layer, $bitrate, $sample_rate, $padding_bit);
        $info['Samples'] = $samples[$simple_version][$layer];
        return $info;
    }

    private static function framesize($layer, $bitrate,$sample_rate,$padding_bit)
    {
        if ($layer==1)
            return intval(((12 * $bitrate*1000 /$sample_rate) + $padding_bit) * 4);
        else //layer 2, 3
            return intval(((144 * $bitrate*1000)/$sample_rate) + $padding_bit);
    }
}

Example code demonstrating usage:

<?php
$mp3file = new MP3File("npr_304314290.mp3");//http://www.npr.org/rss/podcast.php?id=510282
$duration1 = $mp3file->getDurationEstimate();//(faster) for CBR only
$duration2 = $mp3file->getDuration();//(slower) for VBR (or CBR)
echo "duration: $duration1 seconds"."\n";
echo "estimate: $duration2 seconds"."\n";
echo MP3File::formatTime($duration2)."\n";
?>

Source: PHP Calculate Duration of MP3

If you are using Linux / Unix and you have ffmpeg installed, just use this code example:

$time = exec("ffmpeg -i " . escapeshellarg($path) . " 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//");
list($hms, $milli) = explode('.', $time);
list($hours, $minutes, $seconds) = explode(':', $hms);
$total_seconds = ($hours * 3600) + ($minutes * 60) + $seconds;

Answer in English

List of libs for handling audio files:

link

    
22.08.2016 / 16:38
1

I found a class in the GitHub that specifies for this, is one of the most Lightweight I found. An example to use:

<?php
// passa arquivo mp3 para classe
$mp3file = new MP3File("path_do_mp3");

// Pega duração aproximada (desse modo o calculo é mais rápido)
$duration1 = $mp3file->getDurationEstimate();

// Pega a duração exata (processo mais lento que o anterior)
$duration2 = $mp3file->getDuration();

// Finalizando com exibição da duração:
echo "duration: $duration1 seconds"."\n";
echo "estimate: $duration2 seconds"."\n";

// nos testes o getDurationEstimate além de ser mais rápido sempre calculou a mesma duração do getDuration

It is worth remembering that the above class is for MP3 files, for WAV files worth using the small function:

function wavDur($file) 
{
  $fp = fopen($file, 'r');
  if (fread($fp,4) == 'RIFF') 
  {
      fseek($fp, 20);
      $rawheader = fread($fp, 16);
      $header = unpack('vtype/vchannels/Vsamplerate/Vbytespersec/valignment/vbits',$rawheader);
      $pos = ftell($fp);
      while (fread($fp,4) != 'data' && !feof($fp)) 
      {
          $pos++;
          fseek($fp,$pos);
      }
      $rawheader = fread($fp, 4);
      $data = unpack('Vdatasize',$rawheader);
      $sec = $data['datasize']/$header['bytespersec'];
      $minutes = intval(($sec / 60) % 60);
      $seconds = intval($sec % 60);
      return str_pad($minutes,2,'0', STR_PAD_LEFT).':'.str_pad($seconds,2,'0', STR_PAD_LEFT);
  }
}
Looking for a solution for any type of audio file concludes what you already imagined, each audio type has a number of kbps and formulas for different calculations so it is unlikely that there is a solution for all types of files , except a library that deals with the subject, which to my point of view is not pleasant, unless your project explicitly needs to work with various types of audio files. Otherwise just force the input of a specific type of audio

Duration WAV

    
22.08.2016 / 16:33
0

The answers given are good, but they solve the problem in part. In fact, the question is not specifically related to MP3 files, but rather audio files (MP3, WAV, WMA, OGG and the like).

GetID3

I was able to get this information from the audio files through a library called GetID3 .

According to the site :

  

It is a PHP script that extracts useful information from MP3s and other multimedia file formats.

That is, GetID3 , in addition to working for MP3, works for other audio formats, and works to capture the same information as video files.

I've used the library above and can verify that it works.

See a small example:

$getID3 = new getID3;

$audioInfo = $getID3->analyze('meu_arquivo.wav');

$audioInfo['playtime_string'];  // 00:50:00

$audioInfo['playtime_seconds']; // 3000.00
    
09.09.2016 / 14:13