From 3191721e152f9e79e754a19a0a0b8c2b7440af1b Mon Sep 17 00:00:00 2001 From: svollbehr Date: Mon, 14 Apr 2008 23:57:21 +0000 Subject: [PATCH] Add support for ID3v2.3 git-svn-id: http://php-reader.googlecode.com/svn/trunk@75 51a70ab9-7547-0410-9469-37e369ee0574 --- src/ID3/ExtendedHeader.php | 123 +++++++++++++------ src/ID3/Frame.php | 55 ++++++++- src/ID3/Frame/AENC.php | 17 +-- src/ID3/Frame/APIC.php | 7 +- src/ID3/Frame/ASPI.php | 5 +- src/ID3/Frame/AbstractLink.php | 9 +- src/ID3/Frame/AbstractText.php | 21 +++- src/ID3/Frame/COMM.php | 5 +- src/ID3/Frame/COMR.php | 5 +- src/ID3/Frame/ENCR.php | 17 +-- src/ID3/Frame/EQU2.php | 27 ++++- src/ID3/Frame/EQUA.php | 132 +++++++++++++++++++++ src/ID3/Frame/ETCO.php | 5 +- src/ID3/Frame/GEOB.php | 5 +- src/ID3/Frame/GRID.php | 19 +-- src/ID3/Frame/IPLS.php | 168 ++++++++++++++++++++++++++ src/ID3/Frame/LINK.php | 5 +- src/ID3/Frame/MLLT.php | 5 +- src/ID3/Frame/OWNE.php | 5 +- src/ID3/Frame/PCNT.php | 5 +- src/ID3/Frame/POPM.php | 21 ++-- src/ID3/Frame/POSS.php | 5 +- src/ID3/Frame/PRIV.php | 17 +-- src/ID3/Frame/RBUF.php | 5 +- src/ID3/Frame/RVA2.php | 5 +- src/ID3/Frame/RVAD.php | 208 +++++++++++++++++++++++++++++++++ src/ID3/Frame/RVRB.php | 5 +- src/ID3/Frame/SEEK.php | 5 +- src/ID3/Frame/SIGN.php | 5 +- src/ID3/Frame/SYLT.php | 5 +- src/ID3/Frame/SYTC.php | 5 +- src/ID3/Frame/TDAT.php | 55 +++++++++ src/ID3/Frame/TIME.php | 55 +++++++++ src/ID3/Frame/TORY.php | 57 +++++++++ src/ID3/Frame/TRDA.php | 57 +++++++++ src/ID3/Frame/TSIZ.php | 55 +++++++++ src/ID3/Frame/TXXX.php | 85 +++++++++++++- src/ID3/Frame/TYER.php | 55 +++++++++ src/ID3/Frame/WXXX.php | 7 +- src/ID3/Header.php | 58 ++++----- src/ID3/Object.php | 34 ++++-- src/ID3v2.php | 137 +++++++++++----------- 42 files changed, 1338 insertions(+), 243 deletions(-) create mode 100644 src/ID3/Frame/EQUA.php create mode 100644 src/ID3/Frame/IPLS.php create mode 100644 src/ID3/Frame/RVAD.php create mode 100644 src/ID3/Frame/TDAT.php create mode 100644 src/ID3/Frame/TIME.php create mode 100644 src/ID3/Frame/TORY.php create mode 100644 src/ID3/Frame/TRDA.php create mode 100644 src/ID3/Frame/TSIZ.php create mode 100644 src/ID3/Frame/TYER.php diff --git a/src/ID3/ExtendedHeader.php b/src/ID3/ExtendedHeader.php index a5f96d1..9b6dd78 100644 --- a/src/ID3/ExtendedHeader.php +++ b/src/ID3/ExtendedHeader.php @@ -58,21 +58,31 @@ final class ID3_ExtendedHeader extends ID3_Object * in the present file or stream. If frames defined as unique are found in * the present tag, they are to override any corresponding ones found in the * earlier tag. This flag has no corresponding data. + * + * @since ID3v2.4.0 */ - const UPDATE = 128; + const UPDATE = 64; /** - * A flag to denote that a CRC-32 data is included in the extended header. - * The CRC is calculated on all the data between the header and footer as - * indicated by the header's tag length field, minus the extended header. Note - * that this includes the padding (if there is any), but excludes the footer. - * The CRC-32 is stored as an 35 bit synchsafe integer, leaving the upper four - * bits always zeroed. + * @since ID3v2.4.0 A flag to denote that a CRC-32 data is included in the + * extended header. The CRC is calculated on all the data between the header + * and footer as indicated by the header's tag length field, minus the + * extended header. Note that this includes the padding (if there is any), but + * excludes the footer. The CRC-32 is stored as an 35 bit synchsafe integer, + * leaving the upper four bits always zeroed. + * + * @since ID3v2.3.0 The CRC is calculated before unsynchronisation on the data + * between the extended header and the padding, i.e. the frames and only the + * frames. */ - const CRC32 = 64; + const CRC32 = 32; - /** A flag to denote whether or not the tag has restrictions applied on it. */ - const RESTRICTED = 32; + /** + * A flag to denote whether or not the tag has restrictions applied on it. + * + * @since ID3v2.4.0 + */ + const RESTRICTED = 16; /** @var integer */ private $_size; @@ -80,6 +90,9 @@ final class ID3_ExtendedHeader extends ID3_Object /** @var integer */ private $_flags = 0; + /** @var integer */ + private $_padding; + /** @var integer */ private $_crc; @@ -91,33 +104,44 @@ final class ID3_ExtendedHeader extends ID3_Object * from the ID3v2 tag. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; $offset = $this->_reader->getOffset(); $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); - $this->_reader->skip(1); - $this->_flags = $this->_reader->readInt8(); - if ($this->hasFlag(self::UPDATE)) - $this->_reader->skip(1); - if ($this->hasFlag(self::CRC32)) { - $this->_reader->skip(1); - $this->_crc = - Transform::fromInt8($this->_reader->read(1)) * (0xfffffff + 1) + - decodeSynchsafe32(Transform::fromUInt32BE($this->_reader->read(4))); - } - if ($this->hasFlag(self::RESTRICTED)) { - $this->_reader->skip(1); - $this->_restrictions = $this->_reader->readInt8(1); + /* ID3v2.3.0 ExtendedHeader */ + if (isset($this->_options["version"]) && $this->_options["version"] < 4) { + if ($this->_reader->readUInt16BE() == 0x8000) + $this->_flags = self::CRC32; + $this->_padding = $this->_reader->readUInt32BE(); + if ($this->hasFlag(self::CRC32)) + $this->_crc = Transform::readUInt32BE(); } - $this->_reader->skip($this->_size - $this->_reader->getOffset() - $offset); + /* ID3v2.4.0 ExtendedHeader */ + else { + $this->_reader->skip(1); + $this->_flags = $this->_reader->readInt8(); + if ($this->hasFlag(self::UPDATE)) + $this->_reader->skip(1); + if ($this->hasFlag(self::CRC32)) { + $this->_reader->skip(1); + $this->_crc = + Transform::fromInt8($this->_reader->read(1)) * (0xfffffff + 1) + + decodeSynchsafe32(Transform::fromUInt32BE($this->_reader->read(4))); + } + if ($this->hasFlag(self::RESTRICTED)) { + $this->_reader->skip(1); + $this->_restrictions = $this->_reader->readInt8(1); + } + } } /** @@ -249,20 +273,49 @@ final class ID3_ExtendedHeader extends ID3_Object $this->_restrictions = $restrictions; } + /** + * Returns the total padding size, or simply the total tag size excluding the + * frames and the headers. + * + * @return integer + * @deprecated ID3v2.3.0 + */ + public function getPadding() { return $this->_padding; } + + /** + * Sets the total padding size, or simply the total tag size excluding the + * frames and the headers. + * + * @param integer $padding The padding size. + * @deprecated ID3v2.3.0 + */ + public function setPadding($padding) { return $this->_padding = $padding; } + /** * Returns the header raw data. * * @return string */ - public function toString() + public function __toString() { - return Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) . - Transform::toInt8(1) . Transform::toInt8($this->_flags) . - ($this->hasFlag(self::UPDATE) ? "\0" : "") . - ($this->hasFlag(self::CRC32) ? Transform::toInt8(5) . - Transform::toInt8($this->_crc & 0xf0000000 >> 28 & 0xf /* eq >>> 28 */) . - Transform::toUInt32BE(encodeSynchsafe32($this->_crc)) : "") . - ($this->hasFlag(self::RESTRICTED) ? - Transform::toInt8(1) . Transform::toInt8($this->_restrictions) : ""); + /* ID3v2.3.0 ExtendedHeader */ + if (isset($this->_options["version"]) && $this->_options["version"] < 4) { + return Transform::toUInt32BE($this->_size) . + Transform::toUInt16BE($this->hasFlag(self::CRC32) ? 0x8000 : 0) . + Transform::toUInt32BE($this->_padding) . + ($this->hasFlag(self::CRC32) ? Transform::toUInt32BE($this->_crc) : ""); + } + + /* ID3v2.4.0 ExtendedHeader */ + else { + return Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) . + Transform::toInt8(1) . Transform::toInt8($this->_flags) . + ($this->hasFlag(self::UPDATE) ? "\0" : "") . + ($this->hasFlag(self::CRC32) ? Transform::toInt8(5) . + Transform::toInt8($this->_crc & 0xf0000000 >> 28 & 0xf /*eq >>> 28*/) . + Transform::toUInt32BE(encodeSynchsafe32($this->_crc)) : "") . + ($this->hasFlag(self::RESTRICTED) ? + Transform::toInt8(1) . Transform::toInt8($this->_restrictions) : ""); + } } } diff --git a/src/ID3/Frame.php b/src/ID3/Frame.php index 01e32ed..739bf58 100644 --- a/src/ID3/Frame.php +++ b/src/ID3/Frame.php @@ -104,12 +104,16 @@ class ID3_Frame extends ID3_Object /** * This flag indicates whether or not unsynchronisation was applied to this * frame. + * + * @since ID3v2.4.0 */ const UNSYNCHRONISATION = 2; /** * This flag indicates that a data length indicator has been added to the * frame. + * + * @since ID3v2.4.0 */ const DATA_LENGTH_INDICATOR = 1; @@ -135,17 +139,39 @@ class ID3_Frame extends ID3_Object * * @todo Only limited subset of flags are processed. * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) { $this->_identifier = substr(get_class($this), -4); } else { $this->_identifier = $this->_reader->readString8(4); $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); - $this->_flags = $this->_reader->readUInt16BE(); + + /* ID3v2.3.0 Flags; convert to 2.4.0 format */ + if (isset($this->_options["version"]) && $this->_options["version"] < 4) { + $flags = $this->_reader->readUInt16BE(); + if (($flags & 0x8000) == 0x8000) + $this->_flags |= self::DISCARD_ON_TAGCHANGE; + if (($flags & 0x4000) == 0x4000) + $this->_flags |= self::DISCARD_ON_FILECHANGE; + if (($flags & 0x2000) == 0x2000) + $this->_flags |= self::READ_ONLY; + if (($flags & 0x80) == 0x80) + $this->_flags |= self::COMPRESSION; + if (($flags & 0x40) == 0x40) + $this->_flags |= self::ENCRYPTION; + if (($flags & 0x20) == 0x20) + $this->_flags |= self::GROUPING_IDENTITY; + } + + /* ID3v2.4.0 Flags */ + else + $this->_flags = $this->_reader->readUInt16BE(); + $this->_data = $this->_reader->read($this->_size); } } @@ -216,8 +242,29 @@ class ID3_Frame extends ID3_Object */ public function __toString() { + /* ID3v2.3.0 Flags; convert from 2.4.0 format */ + if (isset($this->_options["version"]) && $this->_options["version"] < 4) { + $flags = 0; + if ($this->hasFlag(self::DISCARD_ON_TAGCHANGE)) + $flags = $flags | 0x8000; + if ($this->hasFlag(self::DISCARD_ON_FILECHANGE)) + $flags = $flags | 0x4000; + if ($this->hasFlag(self::READ_ONLY)) + $flags = $flags | 0x2000; + if ($this->hasFlag(self::COMPRESSION)) + $flags = $flags | 0x80; + if ($this->hasFlag(self::ENCRYPTION)) + $flags = $flags | 0x40; + if ($this->hasFlag(self::GROUPING_IDENTITY)) + $flags = $flags | 0x20; + } + + /* ID3v2.4.0 Flags */ + else + $flags = $this->_flags; + return Transform::toString8(substr($this->_identifier, 0, 4), 4) . Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) . - Transform::toUInt16BE($this->_flags) . $this->_data; + Transform::toUInt16BE($flags) . $this->_data; } } diff --git a/src/ID3/Frame/AENC.php b/src/ID3/Frame/AENC.php index af22d95..8e9b7af 100644 --- a/src/ID3/Frame/AENC.php +++ b/src/ID3/Frame/AENC.php @@ -60,7 +60,7 @@ require_once("ID3/Frame.php"); final class ID3_Frame_AENC extends ID3_Frame { /** @var string */ - private $_id; + private $_owner; /** @var integer */ private $_previewStart; @@ -75,15 +75,16 @@ final class ID3_Frame_AENC extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; - list($this->_id, $this->_data) = preg_split("/\\x00/", $this->_data, 2); + list($this->_owner, $this->_data) = preg_split("/\\x00/", $this->_data, 2); $this->_previewStart = substr($this->_data, 0, 2); $this->_previewLength = substr($this->_data, 2, 2); $this->_encryptionInfo = substr($this->_data, 4); @@ -94,14 +95,14 @@ final class ID3_Frame_AENC extends ID3_Frame * * @return string */ - public function getIdentifier() { return $this->_id; } + public function getOwner() { return $this->_owner; } /** * Sets the owner identifier string. * - * @param string $id The owner identifier string. + * @param string $owner The owner identifier string. */ - public function setIdentifier($id) { $this->_id = $id; } + public function setOwner($owner) { $this->_owner = $owner; } /** * Returns the pointer to an unencrypted part of the audio in frames. @@ -162,7 +163,7 @@ final class ID3_Frame_AENC extends ID3_Frame public function __toString() { $this->setData - ($this->_id . "\0" . Transform::toInt16BE($this->_previewStart) . + ($this->_owner . "\0" . Transform::toInt16BE($this->_previewStart) . Transform::toInt16BE($this->_previewLength) . $this->_encryptionInfo); return parent::__toString(); } diff --git a/src/ID3/Frame/APIC.php b/src/ID3/Frame/APIC.php index 20dab0e..4b01912 100644 --- a/src/ID3/Frame/APIC.php +++ b/src/ID3/Frame/APIC.php @@ -97,10 +97,11 @@ final class ID3_Frame_APIC extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; @@ -108,7 +109,7 @@ final class ID3_Frame_APIC extends ID3_Frame $this->_encoding = Transform::fromInt8($this->_data[0]); $this->_mimeType = substr ($this->_data, 1, ($pos = strpos($this->_data, "\0", 1)) - 1); - $this->_pictureType = Transform::fromInt8($this->_data[$pos++]); + $this->_imageType = Transform::fromInt8($this->_data[$pos++]); $this->_data = substr($this->_data, $pos); switch ($this->_encoding) { diff --git a/src/ID3/Frame/ASPI.php b/src/ID3/Frame/ASPI.php index 4c173b6..59ff25c 100644 --- a/src/ID3/Frame/ASPI.php +++ b/src/ID3/Frame/ASPI.php @@ -77,10 +77,11 @@ final class ID3_Frame_ASPI extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) throw new ID3_Exception("Write not supported yet"); diff --git a/src/ID3/Frame/AbstractLink.php b/src/ID3/Frame/AbstractLink.php index 926727f..c8f8b93 100644 --- a/src/ID3/Frame/AbstractLink.php +++ b/src/ID3/Frame/AbstractLink.php @@ -58,13 +58,14 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader !== null) - $this->_link = preg_split("/\\x00/", $this->_data, 1); + $this->_link = implode(preg_split("/\\x00/", $this->_data, 1), ""); } /** @@ -88,7 +89,7 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame */ public function __toString() { - $this->setData($link); + $this->setData($this->_link); return parent::__toString(); } } diff --git a/src/ID3/Frame/AbstractText.php b/src/ID3/Frame/AbstractText.php index 3937dfc..98ca2fd 100644 --- a/src/ID3/Frame/AbstractText.php +++ b/src/ID3/Frame/AbstractText.php @@ -53,20 +53,29 @@ require_once("ID3/Encoding.php"); abstract class ID3_Frame_AbstractText extends ID3_Frame implements ID3_Encoding { - /** @var integer */ - private $_encoding = ID3_Encoding::UTF8; + /** + * The text encoding. + * + * @var integer + */ + protected $_encoding = ID3_Encoding::UTF8; - /** @var string */ - private $_text; + /** + * The text array. + * + * @var string + */ + protected $_text; /** * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/COMM.php b/src/ID3/Frame/COMM.php index b0f4803..468b591 100644 --- a/src/ID3/Frame/COMM.php +++ b/src/ID3/Frame/COMM.php @@ -76,10 +76,11 @@ final class ID3_Frame_COMM extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/COMR.php b/src/ID3/Frame/COMR.php index 56ab4e4..2d57278 100644 --- a/src/ID3/Frame/COMR.php +++ b/src/ID3/Frame/COMR.php @@ -104,10 +104,11 @@ final class ID3_Frame_COMR extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/ENCR.php b/src/ID3/Frame/ENCR.php index 88753e2..13f290d 100644 --- a/src/ID3/Frame/ENCR.php +++ b/src/ID3/Frame/ENCR.php @@ -69,7 +69,7 @@ require_once("ID3/Frame.php"); final class ID3_Frame_ENCR extends ID3_Frame { /** @var string */ - private $_id; + private $_owner; /** @var integer */ private $_method; @@ -81,15 +81,16 @@ final class ID3_Frame_ENCR extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; - list($this->_id, $this->_data) = preg_split("/\\x00/", $this->_data, 2); + list($this->_owner, $this->_data) = preg_split("/\\x00/", $this->_data, 2); $this->_method = Transform::fromInt8($this->_data[0]); $this->_encryptionData = substr($this->_data, 1); } @@ -99,14 +100,14 @@ final class ID3_Frame_ENCR extends ID3_Frame * * @return string */ - public function getIdentifier() { return $this->_id; } + public function getOwner() { return $this->_owner; } /** * Sets the owner identifier string. * - * @param string $id The owner identifier string. + * @param string $owner The owner identifier string. */ - public function setIdentifier($id) { $this->_id = $id; } + public function setOwner($owner) { $this->_owner = $owner; } /** * Returns the method symbol. @@ -147,7 +148,7 @@ final class ID3_Frame_ENCR extends ID3_Frame public function __toString() { parent::setData - ($this->_id . "\0" . Transform::toInt8($this->_method) . + ($this->_owner . "\0" . Transform::toInt8($this->_method) . $this->_encryptionData); return parent::__toString(); } diff --git a/src/ID3/Frame/EQU2.php b/src/ID3/Frame/EQU2.php index c72dcbd..73da81c 100644 --- a/src/ID3/Frame/EQU2.php +++ b/src/ID3/Frame/EQU2.php @@ -82,10 +82,11 @@ final class ID3_Frame_EQU2 extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; @@ -96,8 +97,8 @@ final class ID3_Frame_EQU2 extends ID3_Frame for ($i = 0; $i < strlen($this->_data); $i += 8) $this->_adjustments - [Transform::fromInt16BE(substr($this->_data, $j, 2)) / 2] = - Transform::fromInt16BE(substr($this->_data, $j + 2, 2)) / 512; + [Transform::fromInt16BE(substr($this->_data, $i, 2)) / 2] = + Transform::fromInt16BE(substr($this->_data, $i + 2, 2)) / 512; sort($this->_adjustments); } @@ -151,7 +152,8 @@ final class ID3_Frame_EQU2 extends ID3_Frame * have a value from 0 to 32767 Hz, and the adjustment +/- 64 dB with a * precision of 0.001953125 dB. * - * @return Array + * @param integer $frequency The frequency, in hertz. + * @param integer $adjustment The adjustment, in dB. */ public function addAdjustment($frequency, $adjustment) { @@ -173,4 +175,19 @@ final class ID3_Frame_EQU2 extends ID3_Frame $this->_adjustments[$frequency * 2] = $adjustment * 512; sort($this->_adjustments); } + + /** + * Returns the frame raw data. + * + * @return string + */ + public function __toString() + { + $data = Transform::toInt8($this->_interpolation) . $this->_device . "\0"; + foreach ($this->_adjustments as $frequency => $adjustment) + $data .= + Transform::toInt16BE($frequency) . Transform::toInt16BE($adjustment); + $this->setData($data); + return parent::__toString(); + } } diff --git a/src/ID3/Frame/EQUA.php b/src/ID3/Frame/EQUA.php new file mode 100644 index 0000000..ee882c0 --- /dev/null +++ b/src/ID3/Frame/EQUA.php @@ -0,0 +1,132 @@ +Equalisation frame is another subjective, alignment frame. It + * allows the user to predefine an equalisation curve within the audio file. + * There may only be one EQUA frame in each tag. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_EQUA extends ID3_Frame +{ + /** @var Array */ + private $_adjustments; + + /** + * Constructs the class with given parameters and parses object related data. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader = null, &$options = array()) + { + parent::__construct($reader, $options); + + if ($reader === null) + return; + + $adjustmentBits = Transform::fromInt8($this->_data[0]); //16 + for ($i = 1; $i < strlen($this->_data); $i += 4) { + $frequency = Transform::fromInt16BE(substr($this->_data, $i, 2)); + $this->_adjustments[($frequency & 0x7fff)] = + ($frequency & 0x2000) == 0x2000 ? + Transform::fromInt16BE(substr($this->_data, $j + 2, 2)) : + -Transform::fromInt16BE(substr($this->_data, $j + 2, 2)); + } + } + + /** + * Returns the array containing adjustments having frequencies as keys and + * their corresponding adjustments as values. + * + * @return Array + */ + public function getAdjustments() { return $this->_adjustments; } + + /** + * Adds a volume adjustment setting for given frequency. The frequency can + * have a value from 0 to 32767 Hz. + * + * @param integer $frequency The frequency, in hertz. + * @param integer $adjustment The adjustment, in dB. + */ + public function addAdjustment($frequency, $adjustment) + { + $this->_adjustments[$frequency] = $adjustment; + } + + /** + * Sets the adjustments array. The array must have frequencies as keys and + * their corresponding adjustments as values. The frequency can have a value + * from 0 to 32767 Hz. One frequency should only be described once in the + * frame. + * + * @param Array $adjustments The adjustments array. + */ + public function setAdjustments($adjustments) + { + $this->_adjustments = $adjustments; + } + + /** + * Returns the frame raw data. + * + * @return string + */ + public function __toString() + { + $data = Transform::toInt8(16); + foreach ($this->_adjustments as $frequency => $adjustment) + $data .= Transform::toInt16BE + ($adjustment > 0 ? $frequency | 0x2000 : $frequency & ~0x2000) . + Transform::toInt16BE(abs($adjustment)); + $this->setData($data); + return parent::__toString(); + } +} diff --git a/src/ID3/Frame/ETCO.php b/src/ID3/Frame/ETCO.php index 2159d8c..1cc6eb0 100644 --- a/src/ID3/Frame/ETCO.php +++ b/src/ID3/Frame/ETCO.php @@ -94,10 +94,11 @@ final class ID3_Frame_ETCO extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/GEOB.php b/src/ID3/Frame/GEOB.php index 8be226e..b46a17d 100644 --- a/src/ID3/Frame/GEOB.php +++ b/src/ID3/Frame/GEOB.php @@ -73,10 +73,11 @@ final class ID3_Frame_GEOB extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/GRID.php b/src/ID3/Frame/GRID.php index 60bbe7e..23f4624 100644 --- a/src/ID3/Frame/GRID.php +++ b/src/ID3/Frame/GRID.php @@ -56,7 +56,7 @@ require_once("ID3/Frame.php"); * a digital signature. There may be several GRID frames in a tag but only one * containing the same symbol and only one containing the same owner identifier. * The group symbol must be used somewhere in the tag. See - * {@link ID3_Frame#GROUPING_IDENTITY} for more information. + * {@link ID3_Frame#GROUPING_ownerENTITY} for more information. * * @package php-reader * @subpackage ID3 @@ -68,7 +68,7 @@ require_once("ID3/Frame.php"); final class ID3_Frame_GRID extends ID3_Frame { /** @var string */ - private $_id; + private $_owner; /** @var integer */ private $_group; @@ -80,15 +80,16 @@ final class ID3_Frame_GRID extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; - list($this->_id, $this->_data) = preg_split("/\\x00/", $this->_data, 2); + list($this->_owner, $this->_data) = preg_split("/\\x00/", $this->_data, 2); $this->_group = Transform::fromInt8($this->_data[0]); $this->_groupData = substr($this->_data, 1); } @@ -98,14 +99,14 @@ final class ID3_Frame_GRID extends ID3_Frame * * @return string */ - public function getIdentifier() { return $this->_id; } + public function getOwner() { return $this->_owner; } /** * Sets the owner identifier string. * - * @param string $id The owner identifier string. + * @param string $owner The owner identifier string. */ - public function setIdentifier($id) { $this->_id = $id; } + public function setOwner($owner) { $this->_owner = $owner; } /** * Returns the group symbol. @@ -143,7 +144,7 @@ final class ID3_Frame_GRID extends ID3_Frame public function __toString() { parent::setData - ($this->_id . "\0" . Transform::toInt8($this->_group) . + ($this->_owner . "\0" . Transform::toInt8($this->_group) . $this->_groupData); return parent::__toString(); } diff --git a/src/ID3/Frame/IPLS.php b/src/ID3/Frame/IPLS.php new file mode 100644 index 0000000..726da1a --- /dev/null +++ b/src/ID3/Frame/IPLS.php @@ -0,0 +1,168 @@ +Involved people list is a frame containing the names of those + * involved, and how they were involved. There may only be one IPLS frame in + * each tag. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_IPLS extends ID3_Frame + implements ID3_Encoding +{ + /** @var integer */ + private $_encoding = ID3_Encoding::UTF8; + + /** @var Array */ + private $_people = array(); + + /** + * Constructs the class with given parameters and parses object related data. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader = null, &$options = array()) + { + parent::__construct($reader, $options); + + if ($reader === null) + return; + + $this->_encoding = Transform::fromInt8($this->_data[0]); + $data = array(); + switch ($this->_encoding) { + case self::UTF16: + $data = preg_split + ("/\\x00\\x00/", Transform::fromString16($this->_data)); + break; + case self::UTF16BE: + $data = preg_split + ("/\\x00\\x00/", Transform::fromString16BE($this->_data)); + break; + default: + $data = preg_split("/\\x00/", $this->_data); + } + for ($i = 0; $i < count($data); $i+=2) + $this->_people[] = array($data[$i] => @$data[$i + 1]); + } + + /** + * Returns the text encoding. + * + * @return integer + */ + public function getEncoding() { return $this->_encoding; } + + /** + * Sets the text encoding. + * + * @see ID3_Encoding + * @param integer $encoding The text encoding. + */ + public function setEncoding($encoding) { $this->_encoding = $encoding; } + + /** + * Returns the involved people list as an array. For each person, the array + * contains an entry, which too is an associate array with involvement as its + * key and involvee as its value. + * + * @return Array + */ + public function getPeople() { return $this->_people; } + + /** + * Adds a person with his involvement. + * + * @return string + */ + public function addPerson($involvement, $person) + { + $this->_people[] = array($involvement => $person); + } + + /** + * Sets the involved people list array. For each person, the array must + * contain an associate array with involvement as its key and involvee as its + * value. + * + * @param Array $people The involved people list. + */ + public function setPeople($people) { $this->_people = $people; } + + /** + * Returns the frame raw data. + * + * @return string + */ + public function __toString() + { + $data = Transform::toInt8($this->_encoding); + foreach ($this->_people as $entry) { + foreach ($entry as $key => $val) { + switch ($this->_encoding) { + case self::UTF16: + $data .= Transform::toString16($key . "\0\0" . $val . "\0\0"); + break; + case self::UTF16BE: + $data .= Transform::toString16BE($key . "\0\0" . $val . "\0\0"); + break; + case self::UTF16LE: + $data .= Transform::toString16LE($key . "\0\0" . $val . "\0\0"); + break; + default: + $data .= $key . "\0" . $val . "\0"; + } + } + } + $this->setData($data); + return parent::__toString(); + } +} diff --git a/src/ID3/Frame/LINK.php b/src/ID3/Frame/LINK.php index 7ade550..4ead92d 100644 --- a/src/ID3/Frame/LINK.php +++ b/src/ID3/Frame/LINK.php @@ -98,10 +98,11 @@ final class ID3_Frame_LINK extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/MLLT.php b/src/ID3/Frame/MLLT.php index 5c7ae4c..b916158 100644 --- a/src/ID3/Frame/MLLT.php +++ b/src/ID3/Frame/MLLT.php @@ -85,10 +85,11 @@ final class ID3_Frame_MLLT extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) throw new ID3_Exception("Write not supported yet"); diff --git a/src/ID3/Frame/OWNE.php b/src/ID3/Frame/OWNE.php index 933be3f..75f1702 100644 --- a/src/ID3/Frame/OWNE.php +++ b/src/ID3/Frame/OWNE.php @@ -76,10 +76,11 @@ final class ID3_Frame_OWNE extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/PCNT.php b/src/ID3/Frame/PCNT.php index 3f3e35f..95f17ba 100644 --- a/src/ID3/Frame/PCNT.php +++ b/src/ID3/Frame/PCNT.php @@ -60,10 +60,11 @@ final class ID3_Frame_PCNT extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/POPM.php b/src/ID3/Frame/POPM.php index 5133aea..dec60bb 100644 --- a/src/ID3/Frame/POPM.php +++ b/src/ID3/Frame/POPM.php @@ -65,7 +65,7 @@ require_once("ID3/Frame.php"); final class ID3_Frame_POPM extends ID3_Frame { /** @var string */ - private $_id; + private $_owner; /** @var integer */ private $_rating = 0; @@ -77,15 +77,16 @@ final class ID3_Frame_POPM extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; - list($this->_id, $this->_data) = preg_split("/\\x00/", $this->_data, 2); + list($this->_owner, $this->_data) = preg_split("/\\x00/", $this->_data, 2); $this->_rating = Transform::fromInt8($this->_data[0]); $this->_data = substr($this->_data, 1); @@ -96,18 +97,18 @@ final class ID3_Frame_POPM extends ID3_Frame } /** - * Returns the user identifier string. + * Returns the owner identifier string. * * @return string */ - public function getIdentifier() { return $this->_id; } + public function getOwner() { return $this->_owner; } /** - * Sets the user identifier string. + * Sets the owner identifier string. * - * @param string $id The user identifier string. + * @param string $owner The owner identifier string. */ - public function setIdentifier($id) { return $this->_id = $id; } + public function setOwner($owner) { return $this->_owner = $owner; } /** * Returns the user rating. @@ -150,7 +151,7 @@ final class ID3_Frame_POPM extends ID3_Frame public function __toString() { $this->setData - ($this->_id . "\0" . Transform::toInt8($this->_rating) . + ($this->_owner . "\0" . Transform::toInt8($this->_rating) . ($this->_counter > 4294967295 ? Transform::toInt64BE($this->_counter) : ($this->_counter > 0 ? Transform::toInt32BE($this->_counter) : 0))); diff --git a/src/ID3/Frame/POSS.php b/src/ID3/Frame/POSS.php index 3d7795d..7bdd4d6 100644 --- a/src/ID3/Frame/POSS.php +++ b/src/ID3/Frame/POSS.php @@ -66,10 +66,11 @@ final class ID3_Frame_POSS extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/PRIV.php b/src/ID3/Frame/PRIV.php index e056331..7d95d3b 100644 --- a/src/ID3/Frame/PRIV.php +++ b/src/ID3/Frame/PRIV.php @@ -59,7 +59,7 @@ require_once("ID3/Frame.php"); final class ID3_Frame_PRIV extends ID3_Frame { /** @var string */ - private $_id; + private $_owner; /** @var string */ private $_privateData; @@ -68,15 +68,16 @@ final class ID3_Frame_PRIV extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; - list($this->_id, $this->_privateData) = + list($this->_owner, $this->_privateData) = preg_split("/\\x00/", $this->_data, 2); } @@ -85,14 +86,14 @@ final class ID3_Frame_PRIV extends ID3_Frame * * @return string */ - public function getIdentifier() { return $this->_id; } + public function getOwner() { return $this->_owner; } /** * Sets the owner identifier string. * - * @param string $id The owner identifier string. + * @param string $owner The owner identifier string. */ - public function setIdentifier($id) { $this->_id = $id; } + public function setOwner($owner) { $this->_owner = $owner; } /** * Returns the private binary data associated with the frame. @@ -115,7 +116,7 @@ final class ID3_Frame_PRIV extends ID3_Frame */ public function __toString() { - parent::setData($this->_id . "\0" . $this->_privateData); + parent::setData($this->_owner . "\0" . $this->_privateData); return parent::__toString(); } } diff --git a/src/ID3/Frame/RBUF.php b/src/ID3/Frame/RBUF.php index fbe26f1..a0b7829 100644 --- a/src/ID3/Frame/RBUF.php +++ b/src/ID3/Frame/RBUF.php @@ -91,10 +91,11 @@ final class ID3_Frame_RBUF extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/RVA2.php b/src/ID3/Frame/RVA2.php index c0ba3ff..6d62360 100644 --- a/src/ID3/Frame/RVA2.php +++ b/src/ID3/Frame/RVA2.php @@ -83,10 +83,11 @@ final class ID3_Frame_RVA2 extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/RVAD.php b/src/ID3/Frame/RVAD.php new file mode 100644 index 0000000..08ca4d4 --- /dev/null +++ b/src/ID3/Frame/RVAD.php @@ -0,0 +1,208 @@ +Relative volume adjustment frame is a more subjective function + * than the previous ones. It allows the user to say how much he wants to + * increase/decrease the volume on each channel while the file is played. The + * purpose is to be able to align all files to a reference volume, so that you + * don't have to change the volume constantly. This frame may also be used to + * balance adjust the audio. + * + * There may only be one RVAD frame in each tag. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_RVAD extends ID3_Frame +{ + /** @var Array */ + private $_adjustments; + + /** + * Constructs the class with given parameters and parses object related data. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader = null, &$options = array()) + { + parent::__construct($reader, $options); + + if ($reader === null) + return; + + $flags = Transform::fromInt8($this->_data[0]); + $descriptionBits = Transform::fromInt8($this->_data[0]); //16 + + $this->_adjustments["right"] = + ($flags & 0x20) == 0x20 ? + Transform::fromUInt16BE(substr($this->_data, 0, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 0, 2)); + $this->_adjustments["left"] = + ($flags & 0x10) == 0x10 ? + Transform::fromUInt16BE(substr($this->_data, 2, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 2, 2)); + $this->_adjustments["peakRight"] = + Transform::fromUInt16BE(substr($this->_data, 4, 2)); + $this->_adjustments["peakLeft"] = + Transform::fromUInt16BE(substr($this->_data, 6, 2)); + + if ($this->getSize() <= 8) + return; + + $this->_adjustments["rightBack"] = + ($flags & 0x8) == 0x8 ? + Transform::fromUInt16BE(substr($this->_data, 8, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 8, 2)); + $this->_adjustments["leftBack"] = + ($flags & 0x4) == 0x4 ? + Transform::fromUInt16BE(substr($this->_data, 10, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 10, 2)); + $this->_adjustments["peakRightBack"] = + Transform::fromUInt16BE(substr($this->_data, 12, 2)); + $this->_adjustments["peakLeftBack"] = + Transform::fromUInt16BE(substr($this->_data, 14, 2)); + + if ($this->getSize() <= 16) + return; + + $this->_adjustments["center"] = + ($flags & 0x2) == 0x2 ? + Transform::fromUInt16BE(substr($this->_data, 16, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 16, 2)); + $this->_adjustments["peakCenter"] = + Transform::fromUInt16BE(substr($this->_data, 18, 2)); + + if ($this->getSize() <= 20) + return; + + $this->_adjustments["bass"] = + ($flags & 0x1) == 0x1 ? + Transform::fromUInt16BE(substr($this->_data, 20, 2)) : + -Transform::fromUInt16BE(substr($this->_data, 20, 2)); + $this->_adjustments["peakBass"] = + Transform::fromUInt16BE(substr($this->_data, 22, 2)); + } + + /** + * Returns the array containing the volume adjustments. The array must contain + * the following keys: right, left, peakRight, peakLeft. It may optionally + * contain the following keys: rightBack, leftBack, peakRightBack, + * peakLeftBack, center, peakCenter, bass, and peakBass. + * + * @return Array + */ + public function getAdjustments() { return $this->_adjustments; } + + /** + * Sets the array of volume adjustments. The array must contain the following + * keys: right, left, peakRight, peakLeft. It may optionally contain the + * following keys: rightBack, leftBack, peakRightBack, peakLeftBack, center, + * peakCenter, bass, and peakBass. + * + * @param Array $adjustments The volume adjustments array. + */ + public function setAdjustments($adjustments) + { + $this->_adjustments = $adjustments; + } + + /** + * Returns the frame raw data. + * + * @return string + */ + public function __toString() + { + $flags = 0; + if ($this->_adjustments["right"] > 0) + $flags = $flags | 0x20; + if ($this->_adjustments["left"] > 0) + $flags = $flags | 0x10; + $data = Transform::toInt8(16) . + Transform::toUInt16BE(abs($this->_adjustments["right"])) . + Transform::toUInt16BE(abs($this->_adjustments["left"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakRight"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakLeft"])); + + if (isset($this->_adjustments["rightBack"]) && + isset($this->_adjustments["leftBack"]) && + isset($this->_adjustments["peakRightBack"]) && + isset($this->_adjustments["peakLeftBack"])) { + if ($this->_adjustments["rightBack"] > 0) + $flags = $flags | 0x8; + if ($this->_adjustments["leftBack"] > 0) + $flags = $flags | 0x4; + $data .= + Transform::toUInt16BE(abs($this->_adjustments["rightBack"])) . + Transform::toUInt16BE(abs($this->_adjustments["leftBack"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakRightBack"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakLeftBack"])); + } + + if (isset($this->_adjustments["center"]) && + isset($this->_adjustments["peakCenter"])) { + if ($this->_adjustments["center"] > 0) + $flags = $flags | 0x2; + $data .= + Transform::toUInt16BE(abs($this->_adjustments["center"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakCenter"])); + } + + if (isset($this->_adjustments["bass"]) && + isset($this->_adjustments["peakBass"])) { + if ($this->_adjustments["bass"] > 0) + $flags = $flags | 0x1; + $data .= + Transform::toUInt16BE(abs($this->_adjustments["bass"])) . + Transform::toUInt16BE(abs($this->_adjustments["peakBass"])); + } + $this->setData(Transform::toInt8($flags) . $data); + return parent::__toString(); + } +} diff --git a/src/ID3/Frame/RVRB.php b/src/ID3/Frame/RVRB.php index 74cc233..7c7733f 100644 --- a/src/ID3/Frame/RVRB.php +++ b/src/ID3/Frame/RVRB.php @@ -99,10 +99,11 @@ final class ID3_Frame_RVRB extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/SEEK.php b/src/ID3/Frame/SEEK.php index bf987c9..756f88d 100644 --- a/src/ID3/Frame/SEEK.php +++ b/src/ID3/Frame/SEEK.php @@ -62,10 +62,11 @@ final class ID3_Frame_SEEK extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/SIGN.php b/src/ID3/Frame/SIGN.php index d37fc82..60c37be 100644 --- a/src/ID3/Frame/SIGN.php +++ b/src/ID3/Frame/SIGN.php @@ -67,10 +67,11 @@ final class ID3_Frame_SIGN extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/SYLT.php b/src/ID3/Frame/SYLT.php index ae6efd5..d618fd1 100644 --- a/src/ID3/Frame/SYLT.php +++ b/src/ID3/Frame/SYLT.php @@ -93,10 +93,11 @@ final class ID3_Frame_SYLT extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/SYTC.php b/src/ID3/Frame/SYTC.php index eef0ee4..c937ff8 100644 --- a/src/ID3/Frame/SYTC.php +++ b/src/ID3/Frame/SYTC.php @@ -79,10 +79,11 @@ final class ID3_Frame_SYTC extends ID3_Frame * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; diff --git a/src/ID3/Frame/TDAT.php b/src/ID3/Frame/TDAT.php new file mode 100644 index 0000000..3f6be07 --- /dev/null +++ b/src/ID3/Frame/TDAT.php @@ -0,0 +1,55 @@ +Date frame is a numeric string in the DDMM format containing the + * date for the recording. This field is always four characters long. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TDAT extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/TIME.php b/src/ID3/Frame/TIME.php new file mode 100644 index 0000000..713226c --- /dev/null +++ b/src/ID3/Frame/TIME.php @@ -0,0 +1,55 @@ +Time frame is a numeric string in the HHMM format containing the + * time for the recording. This field is always four characters long. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TIME extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/TORY.php b/src/ID3/Frame/TORY.php new file mode 100644 index 0000000..d273bd6 --- /dev/null +++ b/src/ID3/Frame/TORY.php @@ -0,0 +1,57 @@ +Original release year frame is intended for the year when the + * original recording, if for example the music in the file should be a cover of + * a previously released song, was released. The field is formatted as in the + * {@link ID3_Frame_TYER} frame. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TORY extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/TRDA.php b/src/ID3/Frame/TRDA.php new file mode 100644 index 0000000..1911ed6 --- /dev/null +++ b/src/ID3/Frame/TRDA.php @@ -0,0 +1,57 @@ +Recording dates frame is intended to be used as complement to + * the {@link ID3_Frame_TYER}, {@link ID3_Frame_TDAT} and {@link ID3_Frame_TIME} + * frames. E.g. "4th-7th June, 12th June" in combination with the + * {@link ID3_Frame_TYER} frame. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TRDA extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/TSIZ.php b/src/ID3/Frame/TSIZ.php new file mode 100644 index 0000000..d1551cd --- /dev/null +++ b/src/ID3/Frame/TSIZ.php @@ -0,0 +1,55 @@ +Size frame contains the size of the audiofile in bytes, excluding + * the ID3v2 tag, represented as a numeric string. + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TSIZ extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/TXXX.php b/src/ID3/Frame/TXXX.php index 5658b41..9c2a2b0 100644 --- a/src/ID3/Frame/TXXX.php +++ b/src/ID3/Frame/TXXX.php @@ -56,4 +56,87 @@ require_once("ID3/Frame/AbstractText.php"); * @license http://code.google.com/p/php-reader/wiki/License New BSD License * @version $Rev$ */ -final class ID3_Frame_TXXX extends ID3_Frame_AbstractText {} +final class ID3_Frame_TXXX extends ID3_Frame_AbstractText +{ + /** @var string */ + private $_description; + + /** + * Constructs the class with given parameters and parses object related data. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader = null, &$options = array()) + { + parent::__construct($reader, $options); + + switch ($this->_encoding) { + case self::UTF16: + list($this->_description, $this->_text) = + preg_split("/\\x00\\x00/", $this->_data); + $this->_description = Transform::fromString16($this->_description); + $this->_text = array(Transform::fromString16($this->_text)); + break; + case self::UTF16BE: + list($this->_description, $this->_text) = + preg_split("/\\x00\\x00/", $this->_data); + $this->_description = Transform::fromString16BE($this->_description); + $this->_text = array(Transform::fromString16BE($this->_text)); + break; + default: + list($this->_description, $this->_text) = + preg_split("/\\x00/", $this->_data); + $this->_text = array($this->_text); + } + } + + /** + * Returns the description text. + * + * @return string + */ + public function getDescription() { return $this->_description; } + + /** + * Sets the description text using given encoding. + * + * @param string $description The content description text. + * @param integer $encoding The text encoding. + */ + public function setDescription($description, $encoding = false) + { + $this->_description = $description; + if ($encoding !== false) + $this->_encoding = $encoding; + } + + /** + * Returns the frame raw data. + * + * @return string + */ + public function __toString() + { + $data = Transform::toInt8($this->_encoding); + switch ($this->_encoding) { + case self::UTF16: + $data .= Transform::toString16($this->_description) . "\0\0" . + Transform::toString16($this->_text[0]); + break; + case self::UTF16BE: + $data .= Transform::toString16BE($this->_description) . "\0\0" . + Transform::toString16BE($this->_text[0]); + break; + case self::UTF16LE: + $data .= Transform::toString16LE($this->_description) . "\0\0" . + Transform::toString16LE($this->_text[0]); + break; + default: + $data .= $this->_description . "\0" . $this->_text[0]; + } + $this->setData($data); + return parent::__toString(); + } +} + diff --git a/src/ID3/Frame/TYER.php b/src/ID3/Frame/TYER.php new file mode 100644 index 0000000..350c33d --- /dev/null +++ b/src/ID3/Frame/TYER.php @@ -0,0 +1,55 @@ +Year frame is a numeric string with a year of the recording. This + * frames is always four characters long (until the year 10000). + * + * @package php-reader + * @subpackage ID3 + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + * @deprecated ID3v2.3.0 + */ +final class ID3_Frame_TYER extends ID3_Frame_AbstractText {} diff --git a/src/ID3/Frame/WXXX.php b/src/ID3/Frame/WXXX.php index 6aa36f4..0fc739b 100644 --- a/src/ID3/Frame/WXXX.php +++ b/src/ID3/Frame/WXXX.php @@ -67,10 +67,11 @@ final class ID3_Frame_WXXX extends ID3_Frame_AbstractLink * Constructs the class with given parameters and parses object related data. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { - parent::__construct($reader); + parent::__construct($reader, $options); if ($reader === null) return; @@ -89,8 +90,6 @@ final class ID3_Frame_WXXX extends ID3_Frame_AbstractLink preg_split("/\\x00\\x00/", $this->_data, 2); $this->_description = Transform::fromString16BE($this->_description); break; - case self::UTF8: - case self::ISO88591: default: list($this->_description, $this->_link) = preg_split("/\\x00/", $this->_data); diff --git a/src/ID3/Header.php b/src/ID3/Header.php index c7ce031..a703c24 100644 --- a/src/ID3/Header.php +++ b/src/ID3/Header.php @@ -40,12 +40,8 @@ require_once("ID3/Object.php"); /**#@-*/ /** - * The first part of the ID3v2 tag is the 10 byte tag header. The first three - * bytes of the tag are always "ID3", to indicate that this is an ID3v2 tag, - * directly followed by the two version bytes. The first byte of ID3v2 version - * is its major version, while the second byte is its revision number. All - * revisions are backwards compatible while major versions are not. The version - * is followed by the ID3v2 flags field, of which currently four flags are used. + * The first part of the ID3v2 tag is the 10 byte tag header. The header + * contains information about the tag version and options. * * @package php-reader * @subpackage ID3 @@ -58,24 +54,25 @@ final class ID3_Header extends ID3_Object { /** A flag to denote whether or not unsynchronisation is applied on all frames */ - const UNSYNCHRONISATION = 256; + const UNSYNCHRONISATION = 128; /** A flag to denote whether or not the header is followed by an extended header */ - const EXTENDEDHEADER = 128; + const EXTENDEDHEADER = 64; /** A flag used as an experimental indicator. This flag shall always be set when the tag is in an experimental stage. */ - const EXPERIMENTAL = 64; + const EXPERIMENTAL = 32; - /** A flag to denote whether a footer is present at the very end of the tag */ - const FOOTER = 32; + /** + * A flag to denote whether a footer is present at the very end of the tag. + * + * @since ID3v2.4.0 + */ + const FOOTER = 16; /** @var integer */ - private $_version = 4; - - /** @var integer */ - private $_revision = 0; + private $_version = 4.0; /** @var integer */ private $_flags = 0; @@ -88,33 +85,42 @@ final class ID3_Header extends ID3_Object * from the ID3v2 tag. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { parent::__construct($reader); if ($reader === null) return; - $this->_version = $this->_reader->readInt8(); - $this->_revision = $this->_reader->readInt8(); + $this->_version = $options["version"] = + $this->_reader->readInt8() + $this->_reader->readInt8() / 10; $this->_flags = $this->_reader->readInt8(); $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); + + $this->setOptions($options); } /** - * Returns the tag major version number. + * Returns the tag version number. The version number is in the form of + * major.revision. * * @return integer */ public function getVersion() { return $this->_version; } - + /** - * Returns the tag revision number. + * Sets the tag version number. Supported version numbers are 3.0 and 4.0 + * for ID3v2.3.0 and ID3v2.4.0 standards, respectively. * - * @return integer + * @param integer $version The tag version number in the form of + * major.revision. */ - public function getRevision() { return $this->_revision; } + public function setVersion($version) + { + $this->_version = $this->_options["version"] = $version; + } /** * Checks whether or not the flag is set. Returns true if the flag @@ -159,10 +165,10 @@ final class ID3_Header extends ID3_Object * * @return string */ - protected function __toString() + public function __toString() { - return Transform::toInt8($this->_version) . - Transform::toInt8($this->_revision) . + return Transform::toInt8(floor($this->_version)) . + Transform::toInt8(($this->_version - floor($this->_version)) * 10) . Transform::toInt8($this->_flags) . Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)); } diff --git a/src/ID3/Object.php b/src/ID3/Object.php index ac7a0be..d96825e 100644 --- a/src/ID3/Object.php +++ b/src/ID3/Object.php @@ -59,19 +59,35 @@ abstract class ID3_Object * * @var Array */ - protected $_options = array(); + protected $_options; /** * Constructs the class with given parameters and reads object related data * from the ID3v2 tag. * * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader = null) + public function __construct($reader = null, &$options = array()) { $this->_reader = $reader; + $this->_options = $options; } + /** + * Returns the options array. + * + * @return Array + */ + public function getOptions() { return $this->_options; } + + /** + * Sets the options array. See {@link ID3v2} class for available options. + * + * @param Array $options The options array. + */ + public function setOptions(&$options) { $this->_options = $options; } + /** * Magic function so that $obj->value will work. * @@ -110,9 +126,12 @@ abstract class ID3_Object */ protected function encodeSynchsafe32($val) { - for ($i = 0, $mask = 0xffffff00; $i < 4; $i++, $mask <<= 8) - $val = ($val << 1 & $mask) | ($val << 1 & ~$mask) >> 1; - return $val & 0x7fffffff; + if (!isset($this->_options["version"]) || $this->_options["version"] >= 4) { + for ($i = 0, $mask = 0xffffff00; $i < 4; $i++, $mask <<= 8) + $val = ($val << 1 & $mask) | ($val << 1 & ~$mask) >> 1; + return $val & 0x7fffffff; + } + return $val; } /** @@ -123,8 +142,9 @@ abstract class ID3_Object */ protected function decodeSynchsafe32($val) { - for ($i = 0, $mask = 0xff000000; $i < 3; $i++, $mask >>= 8) - $val = ($val & $mask) >> 1 | ($val & ~$mask); + if (!isset($this->_options["version"]) || $this->_options["version"] >= 4) + for ($i = 0, $mask = 0xff000000; $i < 3; $i++, $mask >>= 8) + $val = ($val & $mask) >> 1 | ($val & ~$mask); return $val; } } diff --git a/src/ID3v2.php b/src/ID3v2.php index 50415bb..47525f8 100644 --- a/src/ID3v2.php +++ b/src/ID3v2.php @@ -106,9 +106,18 @@ final class ID3v2 /** @var string */ private $_filename; - + + /** @var Array */ + private $_options; + /** - * Constructs the ID3v2 class with given file and options. + * Constructs the ID3v2 class with given file and options. The options array + * may also be given as the only parameter. + * + * The following options are currently recognized: + * o version -- The ID3v2 tag version to use in write operation. This option + * is automatically set when a tag is read from a file and defaults to + * version 4.0 for tag write. * * @todo Only limited subset of flags are processed. * @todo Utilize the SEEK frame and search for a footer to find the tag @@ -123,30 +132,52 @@ final class ID3v2 $filename = false; } + $this->_options = &$options; if (($this->_filename = $filename) === false || file_exists($filename) === false) { - $this->_header = new ID3_Header(); - return; - } - - $this->_reader = new Reader($filename); - - if ($this->_reader->readString8(3) != "ID3") - throw new ID3_Exception("File does not contain ID3v2 tag: " . $filename); - - $this->_header = new ID3_Header($this->_reader); - if ($this->_header->getVersion() > 4) - throw new ID3_Exception - ("File does not contain ID3v2 tag of supported version: " . $filename); - if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER)) - $this->_extendedHeader = new ID3_ExtendedHeader($this->_reader); - if ($this->_header->hasFlag(ID3_Header::FOOTER)) - $this->_footer = &$this->_header; // skip footer, and rather copy header - - while ($frame = $this->nextFrame()) { - if (!isset($this->_frames[$frame->identifier])) - $this->_frames[$frame->identifier] = array(); - $this->_frames[$frame->identifier][] = $frame; + $this->_header = new ID3_Header(null, $options); + } else { + $this->_reader = new Reader($filename); + if ($this->_reader->readString8(3) != "ID3") + throw new ID3_Exception + ("File does not contain ID3v2 tag: " . $filename); + + $this->_header = new ID3_Header($this->_reader, $options); + if ($this->_header->getVersion() < 3 || $this->_header->getVersion() > 4) + throw new ID3_Exception + ("File does not contain ID3v2 tag of supported version: " . $filename); + if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER)) + $this->_extendedHeader = + new ID3_ExtendedHeader($this->_reader, $options); + if ($this->_header->hasFlag(ID3_Header::FOOTER)) + $this->_footer = &$this->_header; // skip footer, and rather copy header + + while (true) { + $offset = $this->_reader->getOffset(); + + // Jump off the loop if we reached the end of the tag + if ($offset - 10 >= $this->_header->getSize() - + ($this->hasFooter() ? 10 : 0)) + break; + + // Jump off the loop if we reached the last frame + if ($this->_reader->available() < 4 || Transform::fromUInt32BE + ($identifier = $this->_reader->read(4)) == 0) + break; + $this->_reader->setOffset($offset); + + if (@fopen($filename = "ID3/Frame/" . + strtoupper($identifier) . ".php", "r", true) !== false) + require_once($filename); + if (class_exists($classname = "ID3_Frame_" . $identifier)) + $frame = new $classname($this->_reader, $options); + else + $frame = new ID3_Frame($this->_reader, $options); + + if (!isset($this->_frames[$frame->getIdentifier()])) + $this->_frames[$frame->getIdentifier()] = array(); + $this->_frames[$frame->getIdentifier()][] = $frame; + } } } @@ -192,56 +223,11 @@ final class ID3v2 if (is_subclass_of($extendedHeader, "ID3_ExtendedHeader")) { $this->_header->flags = $this->_header->flags | ID3_Header::EXTENDEDHEADER; + $this->_extendedHeader->setOptions($this->_options); $this->_extendedHeader = $extendedHeader; } else throw new ID3_Exception("Invalid argument"); } - /** - * Checks whether there are frames left in the tag. Returns true if - * there are frames left in the tag, false otherwise. - * - * @return boolean - */ - protected function hasFrames() - { - $offset = $this->_reader->getOffset(); - - // Return false if we reached the end of the tag - if ($offset - 10 >= $this->_header->getSize() - - ($this->hasFooter() ? 10 : 0)) - return false; - - // Return false if we reached the last frame, true otherwise - $res = $this->_reader->readUInt32BE() != 0; - $this->_reader->setOffset($offset); - return $res; - } - - /** - * Returns the next ID3 frame or false if end of tag has been - * reached. Returned objects are of the type ID3_Frame or of any of its child - * types. - * - * @return ID3_Frame|false - */ - protected function nextFrame() - { - $frame = false; - if ($this->hasFrames()) { - $offset = $this->_reader->getOffset(); - $identifier = $this->_reader->readString8(4); - $this->_reader->setOffset($offset); - if (@fopen($filename = "ID3/Frame/" . - strtoupper($identifier) . ".php", "r", true) !== false) - require_once($filename); - if (class_exists($classname = "ID3_Frame_" . $identifier)) - $frame = new $classname($this->_reader); - else - $frame = new ID3_Frame($this->_reader); - } - return $frame; - } - /** * Checks whether there is a frame given as an argument defined in the tag. * Returns true if one ore more frames are present, @@ -299,6 +285,7 @@ final class ID3v2 */ public function addFrame($frame) { + $frame->setOptions($this->_options); if (!$this->hasFrame($frame->getIdentifier())) $this->_frames[$frame->getIdentifier()] = array(); return $this->_frames[$frame->getIdentifier()][] = $frame; @@ -446,9 +433,13 @@ final class ID3v2 else $padlen = ceil(log(0.2 * ($datalen / 1024 + 10), 10) * 1024); } - $data = str_pad($data, $datalen + $padlen, "\0"); + + /* ID3v2.4.0 CRC calculated w/ padding */ + if (!isset($this->_options["version"]) || $this->_options["version"] >= 4) + $data = str_pad($data, $datalen + $padlen, "\0"); if ($this->hasExtendedHeader()) { + $this->_extendedHeader->setPadding($padlen); if ($this->_extendedHeader->hasFlag(ID3_ExtendedHeader::CRC32)) { $crc = crc32($data); if ($crc & 0x80000000) @@ -458,6 +449,10 @@ final class ID3v2 $data = $this->getExtendedHeader() . $data; } + /* ID3v2.3.0 CRC calculated w/o padding */ + if (isset($this->_options["version"]) && $this->_options["version"] < 4) + $data = str_pad($data, $datalen + $padlen, "\0"); + $this->_header->setSize(strlen($data)); return "ID3" . $this->_header . $data .