fix #14 problem

This commit is contained in:
wapmorgan 2019-12-02 03:30:15 +03:00
parent 491bee5706
commit 3d3bae1424
6 changed files with 74 additions and 49 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
vendor
*.mp3

View File

@ -1,7 +1,6 @@
# Mp3Info
The fastest PHP library to get mp3 tags&meta.
[![Composer package](http://composer.network/badge/wapmorgan/mp3info)](https://packagist.org/packages/wapmorgan/mp3info)
[![Latest Stable Version](https://poser.pugx.org/wapmorgan/mp3info/v/stable)](https://packagist.org/packages/wapmorgan/mp3info)
[![Total Downloads](https://poser.pugx.org/wapmorgan/mp3info/downloads)](https://packagist.org/packages/wapmorgan/mp3info)
[![Latest Unstable Version](https://poser.pugx.org/wapmorgan/mp3info/v/unstable)](https://packagist.org/packages/wapmorgan/mp3info)
@ -9,15 +8,15 @@ The fastest PHP library to get mp3 tags&meta.
This class extracts information from mpeg/mp3 audio:
| Audio | id3v1 Tags | id3v2 Tags |
|--------------|------------|------------|
| duration | song | TIT2 |
| bitRate | artist | TPE1 |
| sampleRate | album | TALB |
| channel | year | TYER |
| framesCount | comment | COMM |
| codecVersion | track | TRCK |
| layerVersion | genre | TCON |
| Audio | id3v1 & id3v2 Tags |
|--------------|--------------------|
| duration | song (TIT2) |
| bitRate | artist (TPE1) |
| sampleRate | album (TALB) |
| channel | year (TYER) |
| framesCount | comment (COMM) |
| codecVersion | track (TRCK) |
| layerVersion | genre (TCON) |
1. Usage
2. Performance
@ -31,13 +30,14 @@ This class extracts information from mpeg/mp3 audio:
# Usage
After creating an instance of `Mp3Info` with passing filename as the first argument to the constructor, you can retrieve data from object properties (listed below).
If you need parse tags, you should set 2nd argument this way:
```php
use wapmorgan\Mp3Info\Mp3Info;
$audio = new Mp3Info($fileName, true);
// or omit 2nd argument to increase parsing speed
$audio = new Mp3Info($fileName);
// To get basic audio information
$audio = new Mp3Info('./audio.mp3');
// If you need parse tags, you should set 2nd argument this way:
$audio = new Mp3Info('./audio.mp3', true);
```
And after that access object properties to get audio information:
@ -60,7 +60,6 @@ echo 'Song '.$audio->tags1['song'].' from '.$audio->tags1['artist'].PHP_EOL;
* List of 112 files with constant & variable bitRate with total duration 5:22:28 are parsed in 1.76 sec. *getId3* library against exactly the same mp3 list works for 8x-10x slower - 9.9 sec.
* If you want, there's a very easy way to compare. Just install `nass600/get-id3` package and run console scanner against any folder with audios. It will print time that Mp3Info spent and that getId3.
# Console scanner
To test Mp3Info you can use built-in script that scans dirs and analyzes all mp3-files inside them. To launch script against current folder:

View File

@ -10,6 +10,7 @@
}
},
"require": {
"php": ">=5.4.0",
"ext-mbstring": "*"
},
"require-dev": {

View File

@ -1,13 +1,18 @@
<?php
return array(
1 => array(
1 => array(null, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, false), // MPEG 1 layer 1
2 => array(null, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, false), // MPEG 1 layer 2
3 => array(null, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, false), // MPEG 1 layer 3
),
2 => array(
1 => array(null, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, false), // MPEG 2 layer 1
2 => array(null, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, false), // MPEG 2 layer 2
3 => array(null, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, false), // MPEG 2 layer 3
),
);
use wapmorgan\Mp3Info\Mp3Info;
$data = [
Mp3Info::MPEG_1 => [
1 => [null, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, false], // MPEG 1 layer 1
2 => [null, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, false], // MPEG 1 layer 2
3 => [null, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, false], // MPEG 1 layer 3
],
Mp3Info::MPEG_2 => [
1 => [null, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, false], // MPEG 2 layer 1
2 => [null, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, false], // MPEG 2 layer 2
3 => [null, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, false], // MPEG 2 layer 3
],
];
$data[Mp3Info::MPEG_25] = $data[Mp3Info::MPEG_2];
return $data;

View File

@ -1,5 +1,8 @@
<?php
return array(
1 => array(44100, 48000, 32000, false), // MPEG 1
2 => array(22050, 24000, 16000, false), // MPEG 2
);
use wapmorgan\Mp3Info\Mp3Info;
return [
Mp3Info::MPEG_1 => [44100, 48000, 32000, false], // MPEG 1
Mp3Info::MPEG_2 => [22050, 24000, 16000, false], // MPEG 2
Mp3Info::MPEG_25 => [11025, 12000, 8000, false], // MPEG 2
];

View File

@ -35,25 +35,22 @@ class Mp3Info {
const LAYERS_23_FRAME_SIZE = 1152;
const META = 1;
const TAGS = 2;
const MPEG_1 = 1;
const MPEG_1 = 1;
const MPEG_2 = 2;
const MPEG_25 = 3;
const CODEC_UNDEFINED = 4;
const LAYER_1 = 1;
const LAYER_2 = 2;
const LAYER_3 = 3;
const STEREO = 'stereo';
const JOINT_STEREO = 'joint_stereo';
const DUAL_MONO = 'dual_mono';
const MONO = 'mono';
/**
* Boolean trigger to enable / disable trace output
*/
static public $traceOutput = false;
/**
* @var array
*/
@ -63,6 +60,7 @@ class Mp3Info {
* @var array
*/
static private $_sampleRateTable;
/**
* MPEG codec version (1 or 2)
* @var int
@ -79,60 +77,72 @@ class Mp3Info {
* @var int
*/
public $audioSize;
/**
* Contains audio file name
* @var string
*/
public $_fileName;
/**
* Contains file size
* @var int
*/
public $_fileSize;
/**
* Audio duration in seconds.microseconds (e.g. 3603.0171428571)
* @var float
*/
public $duration;
/**
* Audio bit rate in bps (e.g. 128000)
*/
public $bitRate;
/**
* Audio sample rate in Hz (e.g. 44100)
* @var int
*/
public $sampleRate;
/**
* Contains true if audio has variable bit rate
* @var boolean
*/
public $isVbr = false;
/**
* Channel mode (stereo or dual_mono or joint_stereo or mono)
* @var string
*/
public $channel;
/**
* Number of audio frames in file
* @var int
*/
public $framesCount = 0;
/**
* Contains extra flags
* @var array
*/
public $extraFlags = array();
public $extraFlags = [];
/**
* Audio tags ver. 1 (aka id3v1)
* @var array
*/
public $tags1 = array();
public $tags1 = [];
/**
* Audio tags ver. 2 (aka id3v2)
* @var array
*/
public $tags2 = array();
public $tags2 = [];
/**
* Major version of id3v2 tag (if id3v2 present) (2 or 3 or 4)
* @var int
@ -147,12 +157,12 @@ class Mp3Info {
* List of id3v2 header flags (if id3v2 present)
* @var array
*/
public $id3v2Flags = array();
public $id3v2Flags = [];
/**
* List of id3v2 tags flags (if id3v2 present)
* @var array
*/
public $id3v2TagsFlags = array();
public $id3v2TagsFlags = [];
/**
* Contains time spent to read&extract audio information.
@ -234,7 +244,7 @@ class Mp3Info {
* First frame can lie. Need to fix in future.
* @link https://github.com/wapmorgan/Mp3Info/issues/13#issuecomment-447470813
*/
$framesCount = $this->readFirstFrame($fp);
$framesCount = $this->readMpegFrame($fp);
$this->framesCount = $framesCount !== null
? $framesCount
@ -264,7 +274,7 @@ class Mp3Info {
* @return int Number of frames (if present if first frame)
* @throws \Exception
*/
private function readFirstFrame($fp) {
private function readMpegFrame($fp) {
$pos = ftell($fp);
$headerBytes = $this->readBytes($fp, 4);
@ -282,9 +292,11 @@ class Mp3Info {
} while (ftell($fp) < $limit_pos);
}
if ($headerBytes[0] !== 0xFF || (($headerBytes[1] >> 5) & 0b111) != 0b111) throw new \Exception("At 0x".$pos."(".dechex($pos).") should be the first frame header!");
if ($headerBytes[0] !== 0xFF || (($headerBytes[1] >> 5) & 0b111) != 0b111) throw new \Exception("At 0x".$pos."(".dechex($pos).") should be a frame header!");
switch ($headerBytes[1] >> 3 & 0b11) {
case 0b00: $this->codecVersion = self::MPEG_25; break;
case 0b01: $this->codecVersion = self::CODEC_UNDEFINED; break;
case 0b10: $this->codecVersion = self::MPEG_2; break;
case 0b11: $this->codecVersion = self::MPEG_1; break;
}
@ -310,6 +322,7 @@ class Mp3Info {
case '2stereo': $offset = 21; break;
case '2mono': $offset = 13; break;
}
fseek($fp, $pos + $offset);
if (fread($fp, 4) == self::VBR_SYNC) {
$this->isVbr = true;
@ -320,12 +333,14 @@ class Mp3Info {
$this->extraFlags['VBR'] = (bool)($flagsBytes[3] & 8);
if ($this->extraFlags['frames']) $framesCount = implode(null, unpack('N', fread($fp, 4)));
}
// go to the end of frame
if ($this->layerVersion == 1) {
$this->__cbrFrameSize = floor((12 * $this->bitRate / $this->sampleRate + ($headerBytes[2] >> 1 & 0b1)) * 4);
} else {
$this->__cbrFrameSize = floor(144 * $this->bitRate / $this->sampleRate + ($headerBytes[2] >> 1 & 0b1));
}
fseek($fp, $pos + $this->__cbrFrameSize);
return isset($framesCount) ? $framesCount : null;
@ -653,7 +668,7 @@ class Mp3Info {
* @return boolean True if file is looks correct, False otherwise.
* @throws \Exception
*/
static public function isValidAudio($filename) {
public static function isValidAudio($filename) {
if (!file_exists($filename))
throw new Exception('File '.$filename.' is not present!');
$raw = file_get_contents($filename, false, null, 0, 3);
@ -664,7 +679,7 @@ class Mp3Info {
* @param $frameSize
* @param $raw
*
* @return array
* @return string
*/
private function handleTextFrame($frameSize, $raw)
{