How to find out how long an audio is in PHP?

Asked

Viewed 1,027 times

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 size of the disk file and the duration.

These files can be in several audio formats.

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

But now I need to capture information of duration.

How can I do this in PHP?

Example:

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

$filename = $arquivo->getRealPath();

$info= [
    'tamanho' => $arquivo->getSize(),
    'duracao' => ???
]
  • http://stackoverflow.com/a/12053187/5524514

  • @diegofm is right, now only one answer is missing in English :D

  • I found this class, but apparently only works for files .mp3

  • @Rafaelacioly this one would only work with mp3. The solution presented by diegofm can read the audio files in any format. I’m just waiting for the answer to "positive":D

3 answers

1

I found a class in the Github specified for this, is one of the lightest I found. An example for using:

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

Remember that the above class is for MP3 files, for WAV files valley uses 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);
  }
}

In the search for a solution for any type of audio file concludes what I already imagined, each type of audio has a number of kbps and formulas for calculations like this different, so it is unlikely that there is a solution for all types of files, other than a library that deals with the subject, which to my point of view is not pleasant, unless your project needs to explicitly work with various types of audio files. Otherwise, just force the input of a specific audio type

Duration WAV

  • Good, solves my problem with files mp3. But you know if it would work for files in format wav?

  • I did some tests here and it doesn’t work with wav files, only mp3. I’m analyzing another option to put in the answer.

  • Tip: ID3, as stated in comment :D

  • Yes, I had already taken a look at it, but I did not mention it because I was looking for alternatives, because it is something more 'furry', and since it is clearly a 'planted' question, in case the author himself knows the answer, then I decided to look for something simpler p/ most of the users (taking into consideration that MP3 and WAV formats are more used, along with MPEG - which I have not had time yet to add), and not fully answer the question, since it is known the answer I answered seeking greater simplicity for newer developers.

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 of code demonstrating use:

<?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 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;

Reply in English

List of Libs for handling Audio files:

http://de.php.net/manual/en/refs.utilspec.audio.php

0

The answers are good, but they partly solve the problem. Actually, 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 website:

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

That is, the GetID3, works for other audio formats, even works to capture the same information from video files.

I have already used the library above and can prove 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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.