diff --git a/src/Zend/Media/Flac.php b/src/Zend/Media/Flac.php
new file mode 100644
index 0000000..4ce6e3d
--- /dev/null
+++ b/src/Zend/Media/Flac.php
@@ -0,0 +1,245 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac
+{
+ /** The streaminfo metadata block */
+ const STREAMINFO = 0;
+
+ /** The padding metadata block */
+ const PADDING = 1;
+
+ /** The application metadata block */
+ const APPLICATION = 2;
+
+ /** The seektable metadata block */
+ const SEEKTABLE = 3;
+
+ /** The vorbis comment metadata block */
+ const VORBIS_COMMENT = 4;
+
+ /** The cuesheet metadata block */
+ const CUESHEET = 5;
+
+ /** The picture metadata block */
+ const PICTURE = 6;
+
+ /** @var Zend_Io_Reader */
+ private $_reader;
+
+ /** @var Array */
+ private $_metadataBlocks = array();
+
+ /** @var string */
+ private $_filename = null;
+
+ /**
+ * Constructs the class with given filename.
+ *
+ * @param string|resource|Zend_Io_Reader $filename The path to the file,
+ * file descriptor of an opened file, or a {@link Zend_Io_Reader} instance.
+ * @throws Zend_Io_Exception if an error occur in stream handling.
+ * @throws Zend_Media_Flac_Exception if an error occurs in vorbis bitstream reading.
+ */
+ public function __construct($filename)
+ {
+ if ($filename instanceof Zend_Io_Reader) {
+ $this->_reader = &$filename;
+ } else {
+ $this->_filename = $filename;
+ require_once('Zend/Io/FileReader.php');
+ try {
+ $this->_reader = new Zend_Io_FileReader($filename);
+ } catch (Zend_Io_Exception $e) {
+ $this->_reader = null;
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+
+ $capturePattern = $this->_reader->read(4);
+ if ($capturePattern != 'fLaC') {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception('Not a valid FLAC bitstream');
+ }
+
+ while (true) {
+ $offset = $this->_reader->getOffset();
+ $last = ($tmp = $this->_reader->readUInt8()) >> 7 & 0x1;
+ $type = $tmp & 0x7f;
+ $size = $this->_reader->readUInt24BE();
+
+ $this->_reader->setOffset($offset);
+ switch ($type) {
+ case self::STREAMINFO: // 0
+ require_once 'Zend/Media/Flac/MetadataBlock/Streaminfo.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Streaminfo($this->_reader);
+ break;
+ case self::PADDING: // 1
+ require_once 'Zend/Media/Flac/MetadataBlock/Padding.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Padding($this->_reader);
+ break;
+ case self::APPLICATION: // 2
+ require_once 'Zend/Media/Flac/MetadataBlock/Application.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Application($this->_reader);
+ break;
+ case self::SEEKTABLE: // 3
+ require_once 'Zend/Media/Flac/MetadataBlock/Seektable.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Seektable($this->_reader);
+ break;
+ case self::VORBIS_COMMENT: // 4
+ require_once 'Zend/Media/Flac/MetadataBlock/VorbisComment.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_VorbisComment($this->_reader);
+ break;
+ case self::CUESHEET: // 5
+ require_once 'Zend/Media/Flac/MetadataBlock/Cuesheet.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Cuesheet($this->_reader);
+ break;
+ case self::PICTURE: // 6
+ require_once 'Zend/Media/Flac/MetadataBlock/Picture.php';
+ $this->_metadataBlocks[] = new Zend_Media_Flac_MetadataBlock_Picture($this->_reader);
+ break;
+ default:
+ // break intentionally omitted
+ }
+ $this->_reader->setOffset($offset + 4 /* header */ + $size);
+
+ // Jump off the loop if we reached the end of metadata blocks
+ if ($last === 1) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Checks whether the given metadata block is there. Returns true if one ore more frames are present,
+ * false otherwise.
+ *
+ * @param string $type The metadata block type.
+ * @return boolean
+ */
+ public function hasMetadataBlock($type)
+ {
+ $metadataBlockCount = count($this->_metadataBlocks);
+ for ($i = 0; $i < $metadataBlockCount; $i++) {
+ if ($this->_metadataBlocks[$i]->getType() === $type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns all the metadata blocks as an associate array.
+ *
+ * @return Array
+ */
+ public function getMetadataBlocks()
+ {
+ return $this->_metadataBlocks;
+ }
+
+ /**
+ * Returns an array of metadata blocks frames matching the given type or an empty array if no metadata blocks
+ * matched the type.
+ *
+ * Please note that one may also use the shorthand $obj->type to access the first metadata block with the given
+ * type.
+ *
+ * @param string $type The metadata block type.
+ * @return Array
+ */
+ public function getMetadataBlocksByType($type)
+ {
+ $matches = array();
+ $metadataBlockCount = count($this->_metadataBlocks);
+ for ($i = 0; $i < $metadataBlockCount; $i++) {
+ if ($this->_metadataBlocks[$i]->getType() === $type) {
+ $matches[] = $this->_metadataBlocks[$i];
+ }
+ }
+ return $matches;
+ }
+
+ /**
+ * Magic function so that $obj->value will work.
+ *
+ * @param string $name The field name.
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (method_exists($this, 'get' . ucfirst($name))) {
+ return call_user_func
+ (array($this, 'get' . ucfirst($name)));
+ }
+ if (defined($constant = 'self::' . strtoupper(preg_replace('/(?<=[a-z])[A-Z]/', '_$0', $name)))) {
+ $metadataBlocks = $this->getMetadataBlocksByType(constant($constant));
+ if (isset($metadataBlocks[0])) {
+ return $metadataBlocks[0];
+ }
+ }
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception('Unknown metadata block or field: ' . $name);
+ }
+
+ /**
+ * Magic function so that assignments with $obj->value will work.
+ *
+ * @param string $name The field name.
+ * @param string $value The field value.
+ * @return mixed
+ */
+ public function __set($name, $value)
+ {
+ if (method_exists($this, 'set' . ucfirst(strtolower($name)))) {
+ call_user_func
+ (array($this, 'set' . ucfirst(strtolower($name))), $value);
+ } else {
+ require_once('Zend/Media/Flac/Exception.php');
+ throw new Zend_Media_Flac_Exception('Unknown field: ' . $name);
+ }
+ }
+}
diff --git a/src/Zend/Media/Flac/Exception.php b/src/Zend/Media/Flac/Exception.php
new file mode 100644
index 0000000..5c28c8e
--- /dev/null
+++ b/src/Zend/Media/Flac/Exception.php
@@ -0,0 +1,40 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+class Zend_Media_Flac_Exception extends Zend_Media_Exception
+{}
diff --git a/src/Zend/Media/Flac/MetadataBlock.php b/src/Zend/Media/Flac/MetadataBlock.php
new file mode 100644
index 0000000..079634e
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock.php
@@ -0,0 +1,173 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+abstract class Zend_Media_Flac_MetadataBlock
+{
+ /**
+ * The reader object.
+ *
+ * @var Zend_Io_Reader
+ */
+ protected $_reader;
+
+ /** @var integer */
+ private $_last;
+
+ /** @var integer */
+ private $_type;
+
+ /** @var integer */
+ private $_size;
+
+ /**
+ * Constructs the class with given parameters and reads object related data
+ * from the Flac bitstream.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ $this->_reader = $reader;
+
+ $this->_last = ($tmp = $this->_reader->readUInt8()) >> 7 & 0x1;
+ $this->_type = $tmp & 0x7f;
+ $this->_size = $this->_reader->readUInt24BE();
+ }
+
+ /**
+ * Returns the metadata block type. The type is one of the following.
+ *
+ * o 0: STREAMINFO
+ * o 1: PADDING
+ * o 2: APPLICATION
+ * o 3: SEEKTABLE
+ * o 4: VORBIS_COMMENT
+ * o 5: CUESHEET
+ * o 6: PICTURE
+ * o 7-126: reserved
+ * o 127: invalid, to avoid confusion with a frame sync code
+ *
+ * @return integer
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * Returns the metadata block length without the header, in bytes.
+ *
+ * @return integer
+ */
+ public function getSize()
+ {
+ return $this->_size;
+ }
+
+ /**
+ * Magic function so that $obj->value will work.
+ *
+ * @param string $name The field name.
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (method_exists($this, 'get' . ucfirst($name))) {
+ return call_user_func(array($this, 'get' . ucfirst($name)));
+ } else {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception('Unknown field: ' . $name);
+ }
+ }
+
+ /**
+ * Magic function so that assignments with $obj->value will work.
+ *
+ * @param string $name The field name.
+ * @param string $value The field value.
+ * @return mixed
+ */
+ public function __set($name, $value)
+ {
+ if (method_exists($this, 'set' . ucfirst($name))) {
+ call_user_func(array($this, 'set' . ucfirst($name)), $value);
+ } else {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception('Unknown field: ' . $name);
+ }
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Application.php b/src/Zend/Media/Flac/MetadataBlock/Application.php
new file mode 100644
index 0000000..1a68f33
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Application.php
@@ -0,0 +1,77 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Application extends Zend_Media_Flac_MetadataBlock
+{
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+
+ $this->_identifier = $this->_reader->readUInt32BE();
+ $this->_data = $this->_reader->read($this->getSize() - 4);
+ }
+
+ /**
+ * Returns the application identifier.
+ *
+ * @return integer
+ */
+ public function getIdentifier()
+ {
+ return $this->_identifier;
+ }
+
+ /**
+ * Returns the application data.
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->_data;
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Cuesheet.php b/src/Zend/Media/Flac/MetadataBlock/Cuesheet.php
new file mode 100644
index 0000000..f60f2a2
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Cuesheet.php
@@ -0,0 +1,189 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Cuesheet extends Zend_Media_Flac_MetadataBlock
+{
+ /** @var string */
+ private $_catalogNumber;
+
+ /** @var integer */
+ private $_leadinSamples;
+
+ /** @var boolean */
+ private $_compactDisc;
+
+ /** @var Array */
+ private $_tracks;
+
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+
+ $this->_catalogNumber = rtrim($this->_reader->read(128), "\x00");
+ $this->_leadinSamples = $this->_reader->readInt64BE();
+ $this->_compactDisc = ($this->_reader->readUInt8() >> 7) & 0x1;
+ $this->_reader->skip(258);
+ $tracksLength = $this->_reader->readUInt8();
+ for ($i = 0; $i < $tracksLength; $i++) {
+ $this->_tracks[$i] = array(
+ 'offset' => $this->_reader->readInt64BE(),
+ 'number' => $this->_reader->readUInt8(),
+ 'isrc' => rtrim($this->_reader->read(12), "\x00"),
+ 'type' => (($tmp = $this->_reader->readUInt8()) >> 7 ) & 0x1,
+ 'pre-emphasis' => (($tmp) >> 6 ) & 0x1,
+ 'index' => array());
+ $this->_reader->skip(13);
+ $indexPointsLength = $this->_reader->readUInt8();
+ for ($j = 0; $j < $indexPointsLength; $j++) {
+ $this->_tracks[$i]['index'][$j] = array(
+ 'offset' => $this->_reader->readInt64BE(),
+ 'number' => $this->_reader->readUInt8()
+ );
+ $this->_reader->skip(3);
+ }
+ }
+ }
+
+ /**
+ * Returns the media catalog number, in ASCII printable characters 0x20-0x7e. In general, the media catalog number
+ * may be 0 to 128 bytes long; any unused characters should be right-padded with NUL characters. For CD-DA, this is
+ * a thirteen digit number, followed by 115 NUL bytes.minimum block size (in samples) used in the stream.
+ *
+ * @return string
+ */
+ public function getCatalogNumber()
+ {
+ return $this->_catalogNumber;
+ }
+
+ /**
+ * Returns the number of lead-in samples. This field has meaning only for CD-DA cuesheets; for other uses it should
+ * be 0. For CD-DA, the lead-in is the TRACK 00 area where the table of contents is stored; more precisely, it is
+ * the number of samples from the first sample of the media to the first sample of the first index point of the
+ * first track. According to the Red Book, the lead-in must be silence and CD grabbing software does not usually
+ * store it; additionally, the lead-in must be at least two seconds but may be longer. For these reasons the
+ * lead-in length is stored here so that the absolute position of the first track can be computed. Note that the
+ * lead-in stored here is the number of samples up to the first index point of the first track, not necessarily to
+ * INDEX 01 of the first track; even the first track may have INDEX 00 data.
+ *
+ * @return integer
+ */
+ public function getLeadinSamples()
+ {
+ return $this->_leadinSamples;
+ }
+
+ /**
+ * Returns the minimum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
+ *
+ * @return integer
+ */
+ public function getMinimumFrameSize()
+ {
+ return $this->_minimumFrameSize;
+ }
+
+ /**
+ * Returns the maximum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
+ *
+ * @return integer
+ */
+ public function getMaximumFrameSize()
+ {
+ return $this->_maximumFrameSize;
+ }
+
+ /**
+ * Returns sample rate in Hz. The maximum sample rate is limited by the structure of frame headers to 655350Hz.
+ * Also, a value of 0 is invalid.
+ *
+ * @return integer
+ */
+ public function getSampleRate()
+ {
+ return $this->_sampleRate;
+ }
+
+ /**
+ * Returns whether the CUESHEET corresponds to a Compact Disc or not.
+ *
+ * @return boolean
+ */
+ public function getCompactDisk()
+ {
+ return $this->_compactDisk == 1;
+ }
+
+ /**
+ * Returns an array of values. Each entry is an array containing the following keys.
+ * o offset -- Track offset in samples, relative to the beginning of the FLAC audio stream. It is the offset to
+ * the first index point of the track. (Note how this differs from CD-DA, where the track's offset in the TOC
+ * is that of the track's INDEX 01 even if there is an INDEX 00.) For CD-DA, the offset must be evenly divisible
+ * by 588 samples (588 samples = 44100 samples/sec * 1/75th of a sec).
+ * o number -- Track number. A track number of 0 is not allowed to avoid conflicting with the CD-DA spec, which
+ * reserves this for the lead-in. For CD-DA the number must be 1-99, or 170 for the lead-out; for non-CD-DA,
+ * the track number must for 255 for the lead-out. It is not required but encouraged to start with track 1 and
+ * increase sequentially. Track numbers must be unique within a CUESHEET.
+ * o isrc -- Track ISRC. This is a 12-digit alphanumeric code or an empty string to denote absence of an ISRC.
+ * o type -- The track type: 0 for audio, 1 for non-audio. This corresponds to the CD-DA Q-channel control bit 3.
+ * o pre-emphasis -- The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. This corresponds to the
+ * CD-DA Q-channel control bit 5.
+ * o index -- An array of track index points. There must be at least one index in every track in a CUESHEET except
+ * for the lead-out track, which must have zero. For CD-DA, this number may be no more than 100. Each entry is
+ * an array containing the following keys.
+ * o offset -- Offset in samples, relative to the track offset, of the index point. For CD-DA, the offset must
+ * be evenly divisible by 588 samples (588 samples = 44100 samples/sec * 1/75th of a sec). Note that the
+ * offset is from the beginning of the track, not the beginning of the audio data.
+ * o number -- The index point number. For CD-DA, an index number of 0 corresponds to the track pre-gap. The
+ * first index in a track must have a number of 0 or 1, and subsequently, index numbers must increase by 1.
+ * Index numbers must be unique within a track.
+ *
+ * @return Array
+ */
+ public function getTracks()
+ {
+ return $this->_tracks;
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Padding.php b/src/Zend/Media/Flac/MetadataBlock/Padding.php
new file mode 100644
index 0000000..9cfac61
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Padding.php
@@ -0,0 +1,53 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Padding extends Zend_Media_Flac_MetadataBlock
+{
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Picture.php b/src/Zend/Media/Flac/MetadataBlock/Picture.php
new file mode 100644
index 0000000..de1f5c2
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Picture.php
@@ -0,0 +1,199 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Picture extends Zend_Media_Flac_MetadataBlock
+{
+ /**
+ * The list of picture types.
+ *
+ * @var Array
+ */
+ public static $types = array
+ ('Other', '32x32 pixels file icon (PNG only)', 'Other file icon',
+ 'Cover (front)', 'Cover (back)', 'Leaflet page',
+ 'Media (e.g. label side of CD)', 'Lead artist/lead performer/soloist',
+ 'Artist/performer', 'Conductor', 'Band/Orchestra', 'Composer',
+ 'Lyricist/text writer', 'Recording Location', 'During recording',
+ 'During performance', 'Movie/video screen capture',
+ 'A bright coloured fish', 'Illustration', 'Band/artist logotype',
+ 'Publisher/Studio logotype');
+
+ /** @var integer */
+ private $_type;
+
+ /** @var string */
+ private $_mimeType;
+
+ /** @var string */
+ private $_description;
+
+ /** @var integer */
+ private $_width;
+
+ /** @var integer */
+ private $_height;
+
+ /** @var integer */
+ private $_colorDepth;
+
+ /** @var integer */
+ private $_numberOfColors;
+
+ /** @var integer */
+ private $_dataSize;
+
+ /** @var string */
+ private $_data;
+
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @todo There is the possibility to put only a link to the picture file by
+ * using the MIME type '-->' and having a complete URL instead of picture
+ * data. Support for such needs design considerations.
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+
+ $this->_type = $this->_reader->readUInt32BE();
+ $this->_mimeType = $this->_reader->read($this->_reader->readUInt32BE());
+ $this->_description = $this->_reader->read($this->_reader->readUInt32BE());
+ $this->_width = $this->_reader->readUInt32BE();
+ $this->_height = $this->_reader->readUInt32BE();
+ $this->_colorDepth = $this->_reader->readUInt32BE();
+ $this->_numberOfColors = $this->_reader->readUInt32BE();
+ $this->_data = $this->_reader->read($this->_dataSize = $this->_reader->readUInt32BE());
+ }
+
+ /**
+ * Returns the picture type.
+ *
+ * @return integer
+ */
+ public function getPictureType()
+ {
+ return $this->_pictureType;
+ }
+
+ /**
+ * Returns the MIME type.
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return $this->_mimeType;
+ }
+
+ /**
+ * Returns the picture description.
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ /**
+ * Returns the picture width.
+ *
+ * @return integer
+ */
+ public function getWidth()
+ {
+ return $this->_width;
+ }
+
+ /**
+ * Returns the picture height.
+ *
+ * @return integer
+ */
+ public function getHeight()
+ {
+ return $this->_height;
+ }
+
+ /**
+ * Returns the color depth of the picture in bits-per-pixel.
+ *
+ * @return integer
+ */
+ public function getColorDepth()
+ {
+ return $this->_colorDepth;
+ }
+
+ /**
+ * Returns the number of colors used for indexed-color pictures, or 0 for non-indexed pictures.
+ *
+ * @return integer
+ */
+ public function getNumberOfColors()
+ {
+ return $this->_numberOfColors;
+ }
+
+ /**
+ * Returns the picture data size.
+ *
+ * @return integer
+ */
+ public function getDataSize()
+ {
+ return $this->_dataSize;
+ }
+
+ /**
+ * Returns the picture data.
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->_data;
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Seektable.php b/src/Zend/Media/Flac/MetadataBlock/Seektable.php
new file mode 100644
index 0000000..b785019
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Seektable.php
@@ -0,0 +1,82 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Seektable extends Zend_Media_Flac_MetadataBlock
+{
+ /** @var Array */
+ private $_seekpoints = array();
+
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+
+ $seekpointCount = $this->getSize() / 18;
+ for ($i = 0; $i < $seekpointCount; $i++) {
+ $this->_seekpoints[] = array(
+ 'sampleNumber' => $this->_reader->readInt64BE(),
+ 'offset' => $this->_reader->readInt64BE(),
+ 'numberOfSamples' => $this->_reader->readUInt16BE()
+ );
+ }
+ }
+
+ /**
+ * Returns the seekpoint table. The array consists of items having three keys.
+ *
+ * o sampleNumber -- Sample number of first sample in the target frame, or 0xFFFFFFFFFFFFFFFF for a
+ * placeholder point.
+ * o offset -- Offset (in bytes) from the first byte of the first frame header to the first byte of the
+ * target frame's header.
+ * o numberOfSamples -- Number of samples in the target frame.
+ *
+ * @return Array
+ */
+ public function getSeekpoints()
+ {
+ return $this->_seekpoints;
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/Streaminfo.php b/src/Zend/Media/Flac/MetadataBlock/Streaminfo.php
new file mode 100644
index 0000000..6d5b6fc
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/Streaminfo.php
@@ -0,0 +1,185 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_Streaminfo extends Zend_Media_Flac_MetadataBlock
+{
+ /** @var integer */
+ private $_minimumBlockSize;
+
+ /** @var integer */
+ private $_maximumBlockSize;
+
+ /** @var integer */
+ private $_minimumFrameSize;
+
+ /** @var integer */
+ private $_maximumFrameSize;
+
+ /** @var integer */
+ private $_sampleRate;
+
+ /** @var integer */
+ private $_numberOfChannels;
+
+ /** @var integer */
+ private $_bitsPerSample;
+
+ /** @var integer */
+ private $_numberOfSamples;
+
+ /** @var string */
+ private $_md5Signature;
+
+ /**
+ * Constructs the class with given parameters and parses object related data.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+
+ $this->_minimumBlockSize = $this->_reader->readUInt16BE();
+ $this->_maximumBlockSize = $this->_reader->readUInt16BE();
+ $this->_minimumFrameSize = $this->_reader->readUInt24BE();
+ $this->_maximumFrameSize = $this->_reader->readUInt24BE();
+ $this->_sampleRate = Zend_Bit_Twiddling::getValue(($tmp = $this->_reader->readUInt32BE()), 12, 31);
+ $this->_numberOfChannels = Zend_Bit_Twiddling::getValue($tmp, 9, 11) + 1;
+ $this->_bitsPerSample = Zend_Bit_Twiddling::getValue($tmp, 4, 8) + 1;
+ $this->_numberOfSamples = (Zend_Bit_Twiddling::getValue($tmp, 0, 3) << 32) | $this->_reader->readUInt32BE();
+ $this->_md5Signature = bin2hex($this->_reader->read(16));
+ }
+
+ /**
+ * Returns the minimum block size (in samples) used in the stream.
+ *
+ * @return integer
+ */
+ public function getMinimumBlockSize()
+ {
+ return $this->_minimumBlockSize;
+ }
+
+ /**
+ * Returns the maximum block size (in samples) used in the stream. (Minimum blocksize == maximum blocksize) implies
+ * a fixed-blocksize stream.
+ *
+ * @return integer
+ */
+ public function getMaximumBlockSize()
+ {
+ return $this->_maximumBlockSize;
+ }
+
+ /**
+ * Returns the minimum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
+ *
+ * @return integer
+ */
+ public function getMinimumFrameSize()
+ {
+ return $this->_minimumFrameSize;
+ }
+
+ /**
+ * Returns the maximum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
+ *
+ * @return integer
+ */
+ public function getMaximumFrameSize()
+ {
+ return $this->_maximumFrameSize;
+ }
+
+ /**
+ * Returns sample rate in Hz. The maximum sample rate is limited by the structure of frame headers to 655350Hz.
+ * Also, a value of 0 is invalid.
+ *
+ * @return integer
+ */
+ public function getSampleRate()
+ {
+ return $this->_sampleRate;
+ }
+
+ /**
+ * Returns number of channels. FLAC supports from 1 to 8 channels.
+ *
+ * @return integer
+ */
+ public function getNumberOfChannels()
+ {
+ return $this->_numberOfChannels;
+ }
+
+ /**
+ * Returns bits per sample. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and
+ * decoders only support up to 24 bits per sample.
+ *
+ * @return integer
+ */
+ public function getBitsPerSample()
+ {
+ return $this->_bitsPerSample;
+ }
+
+ /**
+ * Returns total samples in stream. 'Samples' means inter-channel sample, i.e. one second of 44.1Khz audio will
+ * have 44100 samples regardless of the number of channels. A value of zero here means the number of total samples
+ * is unknown.
+ *
+ * @return integer
+ */
+ public function getNumberOfSamples()
+ {
+ return $this->_numberOfSamples;
+ }
+
+ /**
+ * Returns MD5 signature of the unencoded audio data. This allows the decoder to determine if an error exists in
+ * the audio data even when the error does not result in an invalid bitstream.
+ *
+ * @return integer
+ */
+ public function getMd5Signature()
+ {
+ return $this->_md5Signature;
+ }
+}
diff --git a/src/Zend/Media/Flac/MetadataBlock/VorbisComment.php b/src/Zend/Media/Flac/MetadataBlock/VorbisComment.php
new file mode 100644
index 0000000..84e8258
--- /dev/null
+++ b/src/Zend/Media/Flac/MetadataBlock/VorbisComment.php
@@ -0,0 +1,155 @@
+
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id$
+ */
+final class Zend_Media_Flac_MetadataBlock_VorbisComment extends Zend_Media_Flac_MetadataBlock
+{
+ /** @var Zend_Media_Vorbis_Header_Comment */
+ private $_impl;
+
+ /**
+ * Constructs the class with given parameters and parses object related data using the vorbis comment implementation
+ * class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param Zend_Io_Reader $reader The reader object.
+ */
+ public function __construct($reader)
+ {
+ parent::__construct($reader);
+ $this->_impl = new Zend_Media_Vorbis_Header_Comment($this->_reader, array('vorbisContext' => false));
+ }
+
+ /**
+ * Forward all calls to the vorbis comment implementation class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param string $name The method name.
+ * @param Array $arguments The method arguments.
+ * @return mixed
+ */
+ public function __call($name, $arguments)
+ {
+ if (method_exists($this, $name)) {
+ return $this->$name($arguments);
+ }
+ try {
+ return $this->_impl->$name($arguments);
+ } catch (Zend_Media_Vorbis_Exception $e) {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * Forward all calls to the vorbis comment implementation class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param string $name The field name.
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (method_exists($this, 'get' . ucfirst($name))) {
+ return call_user_func(array($this, 'get' . ucfirst($name)));
+ }
+ if (method_exists($this->_impl, 'get' . ucfirst($name))) {
+ return call_user_func(array($this->_impl, 'get' . ucfirst($name)));
+ }
+ try {
+ return $this->_impl->__get($name);
+ } catch (Zend_Media_Vorbis_Exception $e) {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * Forward all calls to the vorbis comment implementation class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param string $name The field name.
+ * @param string $name The field value.
+ * @return mixed
+ */
+ public function __set($name, $value)
+ {
+ if (method_exists($this, 'set' . ucfirst($name))) {
+ call_user_func(array($this, 'set' . ucfirst($name)), $value);
+ } else {
+ try {
+ return $this->_impl->__set($name, $value);
+ } catch (Zend_Media_Vorbis_Exception $e) {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+ }
+
+ /**
+ * Forward all calls to the vorbis comment implementation class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param string $name The field name.
+ * @return boolean
+ */
+ public function __isset($name)
+ {
+ try {
+ return $this->_impl->__isset($name);
+ } catch (Zend_Media_Vorbis_Exception $e) {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * Forward all calls to the vorbis comment implementation class {@link Zend_Media_Vorbis_Header_Comment}.
+ *
+ * @param string $name The field name.
+ */
+ public function __unset($name)
+ {
+ try {
+ $this->_impl->__unset($name);
+ } catch (Zend_Media_Vorbis_Exception $e) {
+ require_once 'Zend/Media/Flac/Exception.php';
+ throw new Zend_Media_Flac_Exception($e->getMessage());
+ }
+ }
+}
diff --git a/src/Zend/Media/Id3/Frame/Apic.php b/src/Zend/Media/Id3/Frame/Apic.php
index 1a6a4eb..f4adade 100644
--- a/src/Zend/Media/Id3/Frame/Apic.php
+++ b/src/Zend/Media/Id3/Frame/Apic.php
@@ -84,7 +84,7 @@ final class Zend_Media_Id3_Frame_Apic extends Zend_Media_Id3_Frame
*
* @todo There is the possibility to put only a link to the image file by
* using the MIME type '-->' and having a complete URL instead of picture
- * data. Support for such needs design considerations.
+ * data. Support for such needs further design considerations.
* @param Zend_Io_Reader $reader The reader object.
* @param Array $options The options array.
*/
diff --git a/src/Zend/Media/Vorbis/Header/Comment.php b/src/Zend/Media/Vorbis/Header/Comment.php
index ca153a0..967e985 100644
--- a/src/Zend/Media/Vorbis/Header/Comment.php
+++ b/src/Zend/Media/Vorbis/Header/Comment.php
@@ -62,17 +62,26 @@ final class Zend_Media_Vorbis_Header_Comment extends Zend_Media_Vorbis_Header
private $_comments;
/** @var integer */
- private $_framingFlag;
+ private $_framingFlag = 1;
/**
- * Constructs the class with given parameters.
+ * Constructs the class with given parameters and reads object related data from the bitstream.
+ *
+ * The following options are currently recognized:
+ * o vorbisContext -- Indicates whether to expect comments to be in the context of a vorbis bitstream or not. This
+ * option can be used to parse vorbis comments in another formats, eg FLAC, that do not use for example the
+ * framing flags. Defaults to true.
*
* @param Zend_Io_Reader $reader The reader object.
+ * @param Array $options Array of options.
*/
- public function __construct($reader)
+ public function __construct($reader, $options = array())
{
- parent::__construct($reader);
-
+ if (!isset($options['vorbisContext']) || $options['vorbisContext']) {
+ parent::__construct($reader);
+ } else {
+ $this->_reader = $reader;
+ }
$this->_vendor = $this->_reader->read($this->_reader->readUInt32LE());
$userCommentListLength = $this->_reader->readUInt32LE();
for ($i = 0; $i < $userCommentListLength; $i++) {
@@ -82,12 +91,24 @@ final class Zend_Media_Vorbis_Header_Comment extends Zend_Media_Vorbis_Header
}
$this->_comments[strtoupper($name)][] = $value;
}
- $this->_framingFlag = $this->_reader->readUInt8() & 0x1;
- if ($this->_framingFlag == 0) {
- require_once 'Zend/Media/Vorbis/Exception.php';
- throw new Zend_Media_Vorbis_Exception('Undecodable Vorbis stream');
+ if (!isset($options['vorbisContext']) || $options['vorbisContext']) {
+ $this->_framingFlag = $this->_reader->readUInt8() & 0x1;
+ if ($this->_framingFlag == 0) {
+ require_once 'Zend/Media/Vorbis/Exception.php';
+ throw new Zend_Media_Vorbis_Exception('Undecodable Vorbis stream');
+ }
+ $this->_reader->skip($this->_packetSize - $this->_reader->getOffset() + 30 /* header */);
}
- $this->_reader->skip($this->_packetSize - $this->_reader->getOffset() + 30 /* header */);
+ }
+
+ /**
+ * Returns the vendor string.
+ *
+ * @return string
+ */
+ public function getVendor()
+ {
+ return $this->_vendor;
}
/**
@@ -124,7 +145,7 @@ final class Zend_Media_Vorbis_Header_Comment extends Zend_Media_Vorbis_Header
*/
public function __isset($name)
{
- return empty($this->_comments[strtoupper($name)]);
+ return count($this->_comments[strtoupper($name)]) > 0;
}
/**