<?php
|
/*---------------------------------------------------------------*/
|
/*
|
Titre : Connaitre la durée d'un ficher MP3
|
|
URL : https://phpsources.net/code_s.php?id=1129
|
Auteur : Place de la Musique
|
Website auteur : https://www.placedelamusique.net/
|
Date édition : 20 Jan 2020
|
Date mise à jour : 20 Jan 2020
|
Rapport de la maj:
|
- fonctionnement du code vérifié
|
*/
|
/*---------------------------------------------------------------*/
|
|
class MpegAudioFrameHeader
|
{
|
/**
|
* MPEG Audio Version 1
|
*/
|
const Version_10 = 1;
|
|
/**
|
* MPEG Audio Version 2
|
*/
|
const Version_20 = 2;
|
|
/**
|
* MPEG Audio Version 2.5
|
*/
|
const Version_25 = 2.5;
|
|
/**
|
* MPEG Audio Profile 1
|
*/
|
const Profile_1 = 1;
|
|
/**
|
* MPEG Audio Profile 2
|
*/
|
const Profile_2 = 2;
|
|
/**
|
* MPEG Audio Profile 3
|
*/
|
const Profile_3 = 3;
|
|
/**
|
* MPEG Audio Stereo Mode
|
*/
|
const Mode_Stereo = 0;
|
|
/**
|
* MPEG Audio Joint Stereo Mode
|
*/
|
const Mode_JointStereo = 1;
|
|
/**
|
* MPEG Audio Dual Channel Mono Mode
|
*/
|
const Mode_DualChannel = 2;
|
|
/**
|
* MPEG Audio Single Channel Mono Mode
|
*/
|
const Mode_SingleChannel = 3;
|
|
/**
|
* MPEG Audio Profile 3 Intensity Stereo Disabled
|
*/
|
const IntensityStereo_Disable = 0;
|
|
/**
|
* MPEG Audio Profile 3 Intensity Stereo Auto Frequency Selection
|
*/
|
const IntensityStereo_Auto = 1;
|
|
/**
|
|
* MPEG Audio Profile 1 & Profile 2 Intensity Stereo Frequency Bands 4 to 31
|
*/
|
const IntensityStereo_Bands4_31 = 2;
|
|
/**
|
|
* MPEG Audio Profile 1 & Profile 2 Intensity Stereo Frequency Bands 8 to 31
|
*/
|
const IntensityStereo_Bands8_31 = 3;
|
|
/**
|
|
* MPEG Audio Profile 1 & Profile 2 Intensity Stereo Frequency Bands 12 to 31
|
*/
|
const IntensityStereo_Bands12_31 = 4;
|
|
/**
|
|
* MPEG Audio Profile 1 & Profile 2 Intensity Stereo Frequency Bands 16 to 31
|
*/
|
const IntensityStereo_Bands16_31 = 5;
|
|
/**
|
* Holds the bit rate of the frame
|
* @var int
|
*/
|
private $bitRate = 0;
|
|
/**
|
* Holds the sample rate of the frame
|
* @var int
|
*/
|
private $sampleRate = 0;
|
|
/**
|
* Holds the MPEG audio version of the frame
|
* @var int
|
*/
|
private $version = -1;
|
|
/**
|
* Holds the MPEG audio profile of the frame
|
* @var int
|
*/
|
private $profile = -1;
|
|
/**
|
* Holds the estimated duration of this frame
|
* @var double
|
*/
|
private $duration = 0.0;
|
|
/**
|
* Holds the frame's data offset in MPEG audio
|
* @var int
|
*/
|
private $offset = 0;
|
|
/**
|
* Holds the frame's data length in MPEG audio
|
* @var int
|
*/
|
private $length = 0;
|
|
/**
|
* Holds the frame's ending padding in bytes
|
* @var int
|
*/
|
private $padding = 0;
|
|
/**
|
* Holds the frame's error protection status
|
* @var bool
|
*/
|
private $errorProtection = false;
|
|
/**
|
* Holds the frame's extra information status
|
* @var bool
|
*/
|
private $privateBit = false;
|
|
/**
|
* Holds the frame's copyrighted work bit status
|
* @var bool
|
*/
|
private $copyrighted = false;
|
|
/**
|
* Holds the frame's copyrighted work originality bit status
|
* @var bool
|
*/
|
private $original = false;
|
|
/**
|
* Holds the frame's channels mode
|
* @var int
|
*/
|
private $mode = self::Mode_Stereo;
|
|
/**
|
* Holds the frame's middle-side stereo joining availability status
|
* @var bool
|
*/
|
private $middleSideStereoJoining = false;
|
|
/**
|
* Holds the frame's intensity stereo operation mode
|
* @var int
|
*/
|
private $intensityStereoMode = self::IntensityStereo_Disable;
|
|
/**
|
|
* Holds the list of every byte along with their equivalent binary representation
|
* @var array
|
*/
|
private static $binaryTable = [];
|
|
/**
|
* Holds the list of standard bit rates for MPEG audio
|
* @var array
|
*/
|
private static $bitRateTable = [];
|
|
/**
|
* Holds the list of standard sample rates for MPEG audio
|
* @var array
|
*/
|
private static $sampleRateTable = [];
|
|
/**
|
* Gets the frame's bit rate in bps
|
* @return int
|
*/
|
public function getBitRate() {
|
return $this->bitRate;
|
}
|
|
/**
|
* Gets the frame's sample rate in Hz
|
* @return int
|
*/
|
public function getSampleRate() {
|
return $this->sampleRate;
|
}
|
|
/**
|
* Gets the frame's MPEG audio version number
|
* @return int
|
*/
|
public function getVersion() {
|
return $this->version;
|
}
|
|
/**
|
* Gets the frame's MPEG audio layer profile number
|
* @return int
|
*/
|
public function getLayerProfile() {
|
return $this->profile;
|
}
|
|
/**
|
* Gets the frame's estimated duration
|
* @return int
|
*/
|
public function getDuration() {
|
return $this->duration;
|
}
|
|
/**
|
* Gets the frame's data offset in MPEG audio
|
* @return int
|
*/
|
public function getOffset() {
|
return $this->offset;
|
}
|
|
/**
|
* Gets the frame's data length in MPEG audio
|
* @return int
|
*/
|
public function getLength() {
|
return $this->length;
|
}
|
|
/**
|
* Gets the frame's ending padding in bytes
|
* @return int
|
*/
|
public function getPadding() {
|
return $this->padding;
|
}
|
|
/**
|
* Gets the frame's error protection status
|
* @return bool
|
*/
|
public function isErrorProtectionEnable() {
|
return $this->errorProtection;
|
}
|
|
/**
|
* Gets the frame's private bit information status
|
* @return bool
|
*/
|
public function isPrivateBitActive() {
|
return $this->privateBit;
|
}
|
|
/**
|
* Gets the frame's copyrighted work bit status
|
* @return bool
|
*/
|
public function isCopyrighted() {
|
return $this->copyrighted;
|
}
|
|
/**
|
* Gets the frame's copyrighted work originality bit status
|
* @return bool
|
*/
|
public function isOriginal() {
|
return $this->original;
|
}
|
|
/**
|
* Gets the frame's channels mode
|
* @return int
|
*/
|
public function getChannelMode() {
|
return $this->mode;
|
}
|
|
/**
|
* Gets the frame's middle side stereo joining availability status
|
* @return bool
|
*/
|
public function isMiddleSideStereoJoiningEnable() {
|
return $this->middleSideStereoJoining;
|
}
|
|
/**
|
* Gets the frame's intensity stereo mode
|
* @return int
|
*/
|
public function getIntensityStereoMode() {
|
return $this->intensityStereoMode;
|
}
|
|
/**
|
|
* Creates a new instance of this class, also fills the binary table for later use
|
*/
|
private function __construct() {
|
if (!self::$binaryTable) {
|
for ($i = 0; $i < 256; $i ++) {
|
self::$binaryTable[chr($i)] = sprintf('%08b', $i);
|
}
|
}
|
if (!self::$bitRateTable) {
|
self::$bitRateTable = array(
|
'0000' => array(0, 0, 0, 0, 0),
|
'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(-1, -1, -1, -1, -1)
|
);
|
}
|
if (!self::$sampleRateTable) {
|
self::$sampleRateTable = array(
|
self::Version_10 => array(
|
'00' => 44100,
|
'01' => 48000,
|
'10' => 32000,
|
'11' => 0
|
),
|
self::Version_20 => array(
|
'00' => 22050,
|
'01' => 24000,
|
'10' => 16000,
|
'11' => 0
|
),
|
self::Version_25 => array(
|
'00' => 11025,
|
'01' => 12000,
|
'10' => 8000,
|
'11' => 0
|
)
|
);
|
}
|
}
|
|
/**
|
|
* Tries to parse and return a new MpegAudioFrameHeader object from the provided data, false on failure
|
* @param string $headerBytes
|
* @param int $offset
|
* @return bool|\falahati\PHPMP3\MpegAudioFrameHeader
|
*/
|
public static function tryParse($headerBytes, $offset) {
|
$frame = new self();
|
$frame->offset = $offset;
|
|
// -------------------------------------------------------------------
|
// Converting bytes to their formatted binary string
|
$headerBits = [];
|
for ($i = 0; $i < strlen($headerBytes); $i ++) {
|
$headerBits[] = self::$binaryTable[$headerBytes[$i]];
|
}
|
|
// -------------------------------------------------------------------
|
// Check header marker
|
if (count($headerBits) < 4 || $headerBits[0] !== '11111111' || substr(
|
$headerBits[1], 0, 3) !== '111') {
|
return false;
|
}
|
|
// -------------------------------------------------------------------
|
// Get version
|
switch (substr($headerBits[1], 3, 2)) {
|
case '01':
|
// Reserved
|
return false;
|
case '00':
|
$frame->version = self::Version_25;
|
break;
|
case '10':
|
$frame->version = self::Version_20;
|
break;
|
case '11':
|
$frame->version = self::Version_10;
|
break;
|
}
|
|
// -------------------------------------------------------------------
|
// Get profile
|
switch (substr($headerBits[1], 5, 2)) {
|
case '01':
|
$frame->profile = self::Profile_3;
|
break;
|
case '00':
|
// Reserved
|
return false;
|
case '10':
|
$frame->profile = self::Profile_2;
|
break;
|
case '11':
|
$frame->profile = self::Profile_1;
|
break;
|
}
|
|
// -------------------------------------------------------------------
|
// Get error protection bit
|
$frame->errorProtection = !!(substr($headerBits[1], 7, 1));
|
|
// -------------------------------------------------------------------
|
// Get bitrate
|
$frame->bitRate = -1;
|
$bitRateIndex = substr($headerBits[2], 0, 4);
|
if ($frame->version == self::Version_10) {
|
switch ($frame->profile) {
|
case self::Profile_1:
|
$frame->bitRate = self::$bitRateTable[$bitRateIndex][0];
|
break;
|
case self::Profile_2:
|
$frame->bitRate = self::$bitRateTable[$bitRateIndex][1];
|
break;
|
case self::Profile_3:
|
$frame->bitRate = self::$bitRateTable[$bitRateIndex][2];
|
break;
|
}
|
} else {
|
switch ($frame->profile) {
|
case self::Profile_1:
|
$frame->bitRate = self::$bitRateTable[$bitRateIndex][3];
|
break;
|
case self::Profile_2:
|
case self::Profile_3:
|
$frame->bitRate = self::$bitRateTable[$bitRateIndex][4];
|
break;
|
}
|
}
|
if ($frame->bitRate <= 0) {
|
// Invalid value or bitrate needs calculation
|
return false;
|
}
|
// Convert kbps to bps
|
$frame->bitRate *= 1000;
|
|
// -------------------------------------------------------------------
|
// Get sample rate
|
$frame->sampleRate = self::$sampleRateTable[$frame->version][substr(
|
$headerBits[2], 4, 2)];
|
if ($frame->sampleRate <= 0) {
|
// Invalid sample rate value
|
return false;
|
}
|
|
// -------------------------------------------------------------------
|
// Get frame padding
|
$frame->padding = substr($headerBits[2], 6, 1) ? 1 : 0;
|
|
// -------------------------------------------------------------------
|
// Get protection bit
|
$frame->privateBit = !!(substr($headerBits[2], 7, 1));
|
|
|
// -------------------------------------------------------------------
|
// Get audio mode
|
switch (substr($headerBits[3], 0, 2)) {
|
case '00':
|
$frame->mode = self::Mode_Stereo;
|
break;
|
case '01':
|
$frame->mode = self::Mode_JointStereo;
|
break;
|
case '10':
|
$frame->mode = self::Mode_DualChannel;
|
break;
|
case '11':
|
$frame->mode = self::Mode_SingleChannel;
|
break;
|
}
|
if ($frame->profile == self::Profile_1 || $frame->profile == self::
|
Profile_2) {
|
$frame->middleSideStereoJoining = false;
|
switch (substr($headerBits[3], 2, 2)) {
|
case '00':
|
$frame->intensityStereoMode = self::IntensityStereo_Bands4_31;
|
break;
|
case '01':
|
$frame->intensityStereoMode = self::IntensityStereo_Bands8_31;
|
break;
|
case '10':
|
$frame->intensityStereoMode = self::IntensityStereo_Bands12_31;
|
break;
|
case '11':
|
$frame->intensityStereoMode = self::IntensityStereo_Bands16_31;
|
break;
|
}
|
} else if ($frame->profile == self::Profile_3) {
|
$frame->intensityStereoMode = substr($headerBits[3], 2, 1) ? self::
|
IntensityStereo_Auto : self::IntensityStereo_Disable;
|
$frame->middleSideStereoJoining = !!(substr($headerBits[3], 3, 1));
|
}
|
|
// -------------------------------------------------------------------
|
// Get copyright information
|
$frame->copyrighted = !!(substr($headerBits[3], 4, 4));
|
$frame->original = !!(substr($headerBits[3], 5, 1));
|
|
// -------------------------------------------------------------------
|
// Calculate frame length
|
if ($frame->profile == self::Profile_1) {
|
$frame->length = (((12 * $frame->bitRate) / $frame->sampleRate) +
|
$frame->padding) * 4;
|
} else if ($frame->profile == self::Profile_2 || $frame->profile ==
|
self::Profile_3) {
|
$frame->length = ((144 * $frame->bitRate) / $frame->sampleRate) +
|
$frame->padding;
|
}
|
$frame->length = floor($frame->length);
|
if ($frame->length <= 0) {
|
// Invalid frame length
|
return false;
|
}
|
|
// -------------------------------------------------------------------
|
// Calculate frame duration
|
$frame->duration = $frame->length * 8 / $frame->bitRate;
|
|
// -------------------------------------------------------------------
|
// Return result
|
return $frame;
|
}
|
}
|
|
/**
|
* This class represents and is able to read and manipulate a MPEG audio
|
* @author Soroush Falahati https://falahati.net
|
* @copyright Soroush Falahati (C) 2017
|
* @license LGPL v3 https://www.gnu.org/licenses/lgpl-3.0.en.html
|
* @link https://github.com/falahati/PHP-MP3
|
*/
|
class MpegAudio
|
{
|
/**
|
* Holds MPEG data in memory
|
* @var string
|
*/
|
private $memory = "";
|
|
/**
|
* Holds an integer value pointing in a specific location in the memory
|
* @var int
|
*/
|
private $memoryPointer = 0;
|
|
/**
|
* Holds the length of the memory
|
* @var int
|
*/
|
private $memoryLength = 0;
|
|
/**
|
* Holds MPEG resource stream
|
* @var resource
|
*/
|
private $resource = null;
|
|
/**
|
|
* Holds an integer number representing the total number of MPEG audio frames
|
* @var int
|
*/
|
private $frames = -1;
|
|
/**
|
|
* Holds a float number representing the total duration of the MPEG audio data
|
* @var double
|
*/
|
private $duration = 0.0;
|
|
/**
|
* Holds an array of frame's starting offsets
|
* @var int[]|array
|
*/
|
private $frameOffsetTable = [];
|
|
/**
|
* Holds an array of frame's starting time
|
* @var double[]|array
|
*/
|
private $frameTimingTable = [];
|
|
/**
|
|
* Loads a MP3 file and returns a newly created MpegAudio object, or false on failure
|
* @param string $path
|
* @return bool|\falahati\PHPMP3\MpegAudio
|
*/
|
public static function fromFile($path) {
|
$inMemory = true;
|
if ($inMemory) {
|
return self::fromData(file_get_contents($path));
|
} else {
|
//return self::fromResource(fopen($path, "cb"));
|
}
|
}
|
|
/**
|
|
* Creates and returns a MpegAudio object and loads binary data, or false on failure
|
* @param string $data
|
* @return bool|\falahati\PHPMP3\MpegAudio
|
*/
|
public static function fromData($data) {
|
if (!is_string($data)) {
|
return false;
|
}
|
$audio = new MpegAudio();
|
$audio->memory = $data;
|
$audio->memoryLength = strlen($audio->memory);
|
return $audio;
|
}
|
|
//public static function fromResource($resource) {
|
// if (!is_resource($resource)) {
|
// return false;
|
// }
|
// $audio = new MpegAudio();
|
// $audio->resource = $resource;
|
// return $audio;
|
//}
|
|
/**
|
* Reads a series of bytes from memory or resource
|
* @param int $length
|
* @param int $index
|
* @return string
|
*/
|
private function read($length = 0, $index = -1) {
|
if ($this->resource === null) {
|
if ($index < 0) {
|
$index = $this->memoryPointer;
|
}
|
if ($length == 0) {
|
$length = $this->memoryLength - $index;
|
}
|
$this->memoryPointer = min($this->memoryLength, $index + $length);
|
return substr($this->memory, $index, $length);
|
} else {
|
// TODO STREAM
|
}
|
}
|
|
/**
|
|
* Writes a series of bytes to the memory or resource, replacing older content or appending to the end
|
* @param string $data
|
* @param int $index
|
* @return int
|
*/
|
private function write($data, $index = -1) {
|
if ($this->resource === null) {
|
$length = strlen($data);
|
$this->slice($length, $index);
|
return $this->insert($data, $index);
|
} else {
|
// TODO STREAM
|
}
|
}
|
|
/**
|
* Inserts a series of bytes to the memory or resource, increasing size
|
* @param string $data
|
* @param int $index
|
* @return int
|
*/
|
private function insert($data, $index = -1) {
|
if ($this->resource === null) {
|
if ($index < 0) {
|
$index = $this->memoryPointer;
|
}
|
$length = strlen($data);
|
$this->memoryPointer = $index + $length;
|
$this->memory = substr($this->memory, 0, $index) . $data . substr(
|
$this->memory, $index);
|
$this->memoryLength += strlen($data);
|
return $length;
|
} else {
|
// TODO STREAM
|
}
|
}
|
|
/**
|
* Removing parts of the memory or resource, decreasing size
|
* @param int $length
|
* @param int $index
|
* @return int
|
*/
|
private function slice($length = 0, $index = -1) {
|
if ($this->resource === null) {
|
if ($index < 0) {
|
$index = $this->memoryPointer;
|
}
|
if ($length == 0) {
|
$length = $this->memoryLength - $index;
|
}
|
$this->memoryPointer = $index;
|
$length = max(min($this->memoryLength - $index, $length), 0);
|
$this->memory = substr($this->memory, 0, $index) . substr($this->
|
memory, $index + $length);
|
$this->memoryLength -= $length;
|
return $length;
|
} else {
|
// TODO STREAM
|
}
|
}
|
|
/**
|
|
* Seeking pointer to a specific location, or returns the current pointer location
|
* @param int $index
|
* @return int|bool
|
*/
|
private function seek($index = -1) {
|
if ($index < 0) {
|
if ($this->resource === null) {
|
return $this->memoryPointer;
|
} else {
|
// TODO STREAM
|
}
|
}
|
if ($this->resource === null) {
|
$this->memoryPointer = $index;
|
return true;
|
} else {
|
// TODO STREAM
|
}
|
}
|
|
/**
|
* Creates an empty MPEG audio class
|
*/
|
public function __construct() {
|
$this->reset();
|
$this->memory = "";
|
}
|
|
/**
|
* Resets all extracted data and marks them for recalculation
|
*/
|
private function reset() {
|
$this->frames = -1;
|
$this->frameTimingTable = [];
|
$this->frameOffsetTable = [];
|
$this->duration = 0.0;
|
}
|
|
/**
|
* Calculate and extract MPEG audio information
|
*/
|
private function analyze() {
|
$offset = $this->getStart();
|
$this->frames = 0;
|
$this->frameOffsetTable = [];
|
$this->frameTimingTable = [];
|
$this->duration = 0.0;
|
if ($offset !== false) {
|
while(true) {
|
$frameHeader = $this->readFrameHeader($offset);
|
if ($frameHeader === false) {
|
// Try recovery
|
$offset = $this->getStart($offset);
|
if ($offset !== false) {
|
continue;
|
}
|
break;
|
}
|
$this->duration += $frameHeader->getDuration();
|
$this->frameOffsetTable[$this->frames] = $frameHeader->getOffset()
|
;
|
$this->frameTimingTable[$this->frames] = $this->duration;
|
$this->frames++;
|
$offset = $frameHeader->getOffset() + $frameHeader->getLength();
|
unset($frameHeader);
|
}
|
}
|
}
|
|
/**
|
|
* Calculates the starting offset of the first frame after the specified offset
|
* @param int $offset
|
* @return bool|int
|
*/
|
private function getStart($offset = 0) {
|
$offset--;
|
while (true) {
|
$offset++;
|
$byte = $this->read(1, $offset);
|
if ($byte === false) {
|
return false;
|
}
|
if ($byte != chr(255)) {
|
continue;
|
}
|
$frameHeader = $this->readFrameHeader($offset);
|
if ($frameHeader === false) {
|
continue;
|
}
|
$frameHeader = $this->readFrameHeader($frameHeader->getOffset() +
|
$frameHeader->getLength());
|
if ($frameHeader === false) {
|
continue;
|
}
|
return $offset;
|
}
|
}
|
|
/**
|
* Reads a frame's header and returns a MpegAudioFrameHeader object
|
* @param int $offset
|
* @return bool|\falahati\PHPMP3\MpegAudioFrameHeader
|
*/
|
private function readFrameHeader($offset) {
|
$bytes = $this->read(4, $offset);
|
return MpegAudioFrameHeader::tryParse($bytes, $offset);
|
}
|
|
/**
|
* Saves this MPEG audio to a file, returns this object
|
* @param string $path
|
* @return \falahati\PHPMP3\MpegAudio
|
*/
|
public function saveFile($path) {
|
if ($this->resource === null) {
|
file_put_contents($path, $this->memory);
|
return $this;
|
} else {
|
fflush($this->resource);
|
// TODO COPY STREAM
|
return $this;
|
}
|
}
|
|
/**
|
|
* Closes all resources and frees the memory, returns MPEG audio as binary string, or a boolean value indicating the operation success
|
* @return bool|string
|
*/
|
public function close() {
|
if ($this->resource === null) {
|
$data = $this->memory;
|
$this->memory = "";
|
$this->memoryLength = 0;
|
$this->memoryPointer = 0;
|
return $data;
|
}
|
if ($this->resource !== null && fclose($this->resource)) {
|
$this->resource = null;
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* Gets the number of frames in this MPEG audio
|
* @return int
|
*/
|
public function getFrameCounts() {
|
if ($this->frames < 0) {
|
$this->analyze();
|
}
|
return $this->frames;
|
}
|
|
/**
|
* Gets the total duration of this MPEG audio in seconds
|
* @return double
|
*/
|
public function getTotalDuration() {
|
if ($this->getFrameCounts()) {
|
return $this->duration;
|
}
|
return 0.0;
|
}
|
|
/**
|
|
* Gets a MpegAudioFrameHeader object reperesenting the header of an MPEG audio frame, or false or failure
|
* @param int $index
|
* @return bool|\falahati\PHPMP3\MpegAudioFrameHeader
|
*/
|
public function getFrameHeader($index) {
|
if ($index >= 0 && $index < $this->getFrameCounts()) {
|
return $this->readFrameHeader($this->frameOffsetTable[$index]);
|
}
|
return false;
|
}
|
|
/**
|
|
* Gets a frame's data (including header) as a binary string, or false or failure
|
* @param int $index
|
* @return bool|string
|
*/
|
public function getFrameData($index) {
|
$frameHeader = $this->getFrameHeader($index);
|
if ($frameHeader !== false) {
|
return $this->read($frameHeader->getOffset(), $frameHeader->
|
getLength());
|
}
|
return false;
|
}
|
|
/**
|
* Removes a set of frames from this MPEG audio, returns this object
|
* @param int $index
|
* @param int $count
|
* @return \falahati\PHPMP3\MpegAudio
|
*/
|
public function removeFrame($index, $count = 1) {
|
if ($count < 0) {
|
$index += $count;
|
$count *= -1;
|
}
|
if ($index < 0 || $index >= $this->getFrameCounts()) {
|
return $this;
|
}
|
$count = min($this->getFrameCounts() - $index, $count);
|
if ($count == 0) {
|
return $this;
|
}
|
$firstFrameHeader = $this->getFrameHeader($index);
|
$lastFrameHeader = $this->getFrameHeader($index + ($count - 1));
|
$this->slice(($lastFrameHeader->getOffset() + $lastFrameHeader->
|
getLength()) - $firstFrameHeader->getOffset(), $firstFrameHeader->getOffset());
|
$this->reset();
|
return $this;
|
}
|
|
/**
|
|
* Appends a potion of a MPEG audio to this MPEG audio, returns this object
|
* @param \falahati\PHPMP3\MpegAudio $srcAudio
|
* @param int $index
|
* @param int $length
|
* @return \falahati\PHPMP3\MpegAudio
|
*/
|
public function append(\falahati\PHPMP3\MpegAudio $srcAudio, $index = 0,
|
$length = -1) {
|
if ($index < 0 || $index >= $srcAudio->getFrameCounts()) {
|
return $this;
|
}
|
if ($length < 0) {
|
$length = $srcAudio->getFrameCounts() - $index;
|
}
|
$length = min($srcAudio->getFrameCounts() - $index, $length);
|
|
$srcFirstFrameHeader = $srcAudio->getFrameHeader($index);
|
$srcLastFrameHeader = $srcAudio->getFrameHeader($index + ($length - 1)
|
);
|
$data = $srcAudio->read(($srcLastFrameHeader->getOffset() +
|
$srcLastFrameHeader->getLength()) - $srcFirstFrameHeader->getOffset(),
|
$srcFirstFrameHeader->getOffset());
|
if ($data) {
|
$endOfStream = 0;
|
if ($this->getFrameCounts() > 0) {
|
$frameHeader = $this->getFrameHeader($this->getFrameCounts() - 1);
|
if ($frameHeader !== false) {
|
$endOfStream = $frameHeader->getOffset() + $frameHeader->
|
getLength();
|
}
|
}
|
$this->insert($data, $endOfStream);
|
}
|
return $this;
|
}
|
|
/**
|
|
* Trims this MPEG audio by removing all frames except the parts that are selected by time in seconds, returns this object
|
* @param int $startTime
|
* @param int $duration
|
* @return \falahati\PHPMP3\MpegAudio
|
*/
|
public function trim($startTime, $duration = 0) {
|
if ($startTime < 0) {
|
$startTime = $this->getTotalDuration() + $startTime;
|
}
|
if ($duration <= 0) {
|
$duration = $this->getTotalDuration() - $startTime;
|
}
|
$endTime = min($startTime + $duration, $this->getTotalDuration());
|
$startIndex = 0;
|
$endIndex = 0;
|
foreach ($this->frameTimingTable as $frameIndex => $frameTiming) {
|
if ($frameTiming <= $startTime) {
|
$startIndex = $frameIndex;
|
} else if ($frameTiming >= $endTime) {
|
$endIndex = $frameIndex;
|
break;
|
}
|
}
|
$this->removeFrame($endIndex, $this->getFrameCounts() - $endIndex);
|
$this->removeFrame(0, $startIndex);
|
return $this;
|
}
|
|
/**
|
|
* Gets metadata stored at the begining of the MPEG audio as a binary string, or false on failure
|
* @return bool|string
|
*/
|
public function getBeginingTags() {
|
$start = $this->getStart();
|
if ($start === false) {
|
return false;
|
}
|
return $this->read($start, 0);
|
}
|
|
/**
|
|
* Gets metadata stored at the end of the MPEG audio as a binary string, or false on failure
|
* @return bool|string
|
*/
|
public function getEndingTags() {
|
$frames = $this->getFrameCounts();
|
if ($frames === 0) {
|
return false;
|
}
|
$frame = $this->getFrameHeader($frames - 1);
|
if ($frame === false) {
|
return false;
|
}
|
return $this->read(0, $frame->getOffset() + $frame->getLength());
|
}
|
|
/**
|
|
* Removes metadata stored at the begining and the end of the MPEG audio, returns this object
|
* @return \falahati\PHPMP3\MpegAudio
|
*/
|
public function stripTags() {
|
$frames = $this->getFrameCounts();
|
if ($frames > 0) {
|
$frame = $this->getFrameHeader($frames - 1);
|
if ($frame !== false) {
|
$this->slice(0, $frame->getOffset() + $frame->getLength());
|
}
|
}
|
$start = $this->getStart();
|
if ($start !== false && $start > 0) {
|
$this->slice($start, 0);
|
}
|
$this->reset();
|
return $this;
|
}
|
}
|
|
?>
|
|
|
|
Invité
30 Nov 2021 à 19:34Merci beaucoup !