mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-05 19:41:55 +02:00
1886 lines
99 KiB
PHP
1886 lines
99 KiB
PHP
<?php
|
||
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
|
||
// +----------------------------------------------------------------------+
|
||
// | PHP version 5 |
|
||
// +----------------------------------------------------------------------+
|
||
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
|
||
// +----------------------------------------------------------------------+
|
||
// | This source file is subject to version 2 of the GPL license, |
|
||
// | that is bundled with this package in the file license.txt and is |
|
||
// | available through the world-wide-web at the following url: |
|
||
// | http://www.gnu.org/copyleft/gpl.html |
|
||
// +----------------------------------------------------------------------+
|
||
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
|
||
// +----------------------------------------------------------------------+
|
||
// | Authors: James Heinrich <info<66>getid3*org> |
|
||
// | Allan Hansen <ah<61>artemis*dk> |
|
||
// +----------------------------------------------------------------------+
|
||
// | write.id3v1.php |
|
||
// | writing module for id3v1 tags |
|
||
// | dependencies: module.tag.id3v1.php. |
|
||
// +----------------------------------------------------------------------+
|
||
//
|
||
// $Id: write.id3v2.php,v 1.9 2006/12/25 23:44:23 ah Exp $
|
||
|
||
|
||
|
||
class getid3_write_id3v2 extends getid3_handler_write
|
||
{
|
||
// NOTE: This module ONLY writes tags in UTF-8. All strings must be UTF-8 encoded.
|
||
|
||
// For multiple values, specify "array of type" instead of type for all T??? and IPLS params except TXXX.
|
||
/**2.4
|
||
// For multiple values, specify "array of type" instead of type for all T??? params except TXXX.
|
||
*/
|
||
|
||
|
||
// Identification frames
|
||
public $content_group_description; // TIT1 string
|
||
public $title; // TIT2 string
|
||
public $subtitle; // TIT3 string
|
||
public $album; // TALB string
|
||
public $original_album_title; // TOAL string
|
||
public $track; // TRCK integer or "integer/integer" e.g. "10/12"
|
||
public $part_of_set; // TPOS integer or "integer/integer" e.g. "10/12"
|
||
public $isrc; // TSRC string
|
||
|
||
// Involved persons frames
|
||
public $artist; // TPE1 string
|
||
public $band; // TPE2 string
|
||
public $conductor; // TPE3 string
|
||
public $remixer; // TPE4 string
|
||
public $original_artist; // TOPE string
|
||
public $lyricist; // TEXT string
|
||
public $original_lyricist; // TOLY string
|
||
public $composer; // TCOM string
|
||
public $encoded_by; // TENC string
|
||
|
||
// Derived and subjective properties frames
|
||
public $beats_per_minute; // TBPM integer
|
||
public $length; // TLEN integer
|
||
public $initial_key; // TKEY string
|
||
public $language; // TLAN string - ISO-639-2
|
||
public $genre; // TCON string or integer
|
||
public $file_type; // TFLT string
|
||
public $media_type; // TMED string
|
||
|
||
// Rights and license frames
|
||
public $copyright; // TCOP string - must begin with YEAR and a space
|
||
public $date; // TDAT string - DDMM
|
||
public $year; // TYER string - YYYY
|
||
public $original_release_year; // TORY string - YYYY
|
||
public $recording_dates; // TRDA string
|
||
public $time; // TIME string - HHMM
|
||
public $publisher; // TPUB string
|
||
public $file_owner; // TOWN string
|
||
public $internet_radio_station_name; // TRSN string
|
||
public $internet_radio_station_owner; // TRSO string
|
||
public $involved_people_list; // IPLS string
|
||
|
||
// Other text frames
|
||
public $original_filename; // TOFN string
|
||
public $playlist_delay; // TDLY integer
|
||
public $encoder_settings; // TSSE string
|
||
|
||
// User defined text information frame
|
||
public $user_text; // TXXX array of ( unique_description(string) => value(string) )
|
||
|
||
// Comments
|
||
public $comment; // COMM
|
||
|
||
// URL link frames - details
|
||
public $commercial_information; // WCOM url(string)
|
||
public $copyright_information; // WCOP url(string)
|
||
public $url_file; // WOAF url(string)
|
||
public $url_artist; // WOAR url(string)
|
||
public $url_source; // WOAS url(string)
|
||
public $url_station; // WORS url(string)
|
||
public $payment; // WPAY url(string)
|
||
public $url_publisher; // WPUB url(string)
|
||
|
||
// User defined URL link frame
|
||
public $url_user; // WXXX array of ( unique_description(string) => url(string) )
|
||
|
||
|
||
// Unique file identifier
|
||
public $unique_file_identifier; // UFID
|
||
|
||
// Music CD identifier
|
||
public $music_cd_identifier; // MCDI
|
||
|
||
// Event timing codes
|
||
public $event_timing_codes; // ETCO
|
||
|
||
// MPEG location lookup table
|
||
public $mpeg_location_lookup_table; // MLLT
|
||
|
||
// Synchronised tempo codes
|
||
public $synchronised_tempo_codes; // SYTC
|
||
|
||
// Unsynchronised lyrics/text transcription
|
||
public $unsynchronised_lyrics; // USLT
|
||
|
||
// Synchronised lyrics/text
|
||
public $synchronised_lyrics; // SYLT
|
||
|
||
// Relative volume adjustment (1)
|
||
public $relative_volume_adjustment; // RVAD
|
||
|
||
// Equalisation (1)
|
||
public $equalisation; // EQUA
|
||
|
||
// Reverb
|
||
public $reverb; // RVRB
|
||
|
||
// Attached picture
|
||
public $attached_picture; // APIC
|
||
|
||
// General encapsulated object
|
||
public $general_encapsulated_object; // GEOB
|
||
|
||
// Play counter
|
||
public $play_counter; // PCNT
|
||
|
||
// Popularimeter
|
||
public $popularimeter; // POPM
|
||
|
||
// Recommended buffer size
|
||
public $recommended_buffer_size; // RBUF
|
||
|
||
// Audio encryption
|
||
public $audio_encryption; // AENC
|
||
|
||
// Linked information
|
||
public $linked_information; // LINK
|
||
|
||
// Position synchronisation frame
|
||
public $position_synchronisation; // POSS
|
||
|
||
// Terms of use frame
|
||
public $terms_of_use; // USER
|
||
|
||
// Ownership frame
|
||
public $ownership; // OWNE
|
||
|
||
// Commercial frame
|
||
public $commercial; // COMR
|
||
|
||
// Encryption method registration
|
||
public $encryption_method_registration; // ENCR
|
||
|
||
// Group identification registration
|
||
public $group_identification_registration; // GRID
|
||
|
||
// Private frame
|
||
public $private; // PRIV
|
||
|
||
|
||
/**2.4
|
||
// Identification frames
|
||
public $content_group_description; // TIT1 string
|
||
public $title; // TIT2 string
|
||
public $subtitle; // TIT3 string
|
||
public $album; // TALB string
|
||
public $original_album_title; // TOAL string
|
||
public $track; // TRCK integer or "integer/integer" e.g. "10/12"
|
||
public $part_of_set; // TPOS integer or "integer/integer" e.g. "10/12"
|
||
public $set_subtitle; // TSST string
|
||
public $isrc; // TSRC string
|
||
|
||
// Involved persons frames
|
||
public $artist; // TPE1 string
|
||
public $band; // TPE2 string
|
||
public $conductor; // TPE3 string
|
||
public $remixer; // TPE4 string
|
||
public $original_artist; // TOPE string
|
||
public $lyricist; // TEXT string
|
||
public $original_lyricist; // TOLY string
|
||
public $composer; // TCOM string
|
||
public $musician_credits_list; // TMCL string
|
||
public $involved_people_list; // TIPL string
|
||
public $encoded_by; // TENC string
|
||
|
||
// Derived and subjective properties frames
|
||
public $beats_per_minute; // TBPM integer
|
||
public $length; // TLEN integer
|
||
public $initial_key; // TKEY string
|
||
public $language; // TLAN string - ISO-639-2
|
||
public $genre; // TCON string or integer
|
||
public $file_type; // TFLT string
|
||
public $media_type; // TMED string
|
||
public $mood; // TMOO string
|
||
|
||
// Rights and license frames
|
||
public $copyright; // TCOP string - must begin with YEAR and a space
|
||
// TPRO strign - must begin with YEAR and a space
|
||
public $publisher; // TPUB string
|
||
public $file_owner; // TOWN string
|
||
public $internet_radio_station_name; // TRSN string
|
||
public $internet_radio_station_owner; // TRSO string
|
||
|
||
// Other text frames
|
||
public $original_filename; // TOFN string
|
||
public $playlist_delay; // TDLY integer
|
||
public $encoding_time; // TDEN timestamp(string) - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm, yyyy-MM-ddTHH:mm:ss. All time stamps are UTC.
|
||
public $original_release_time; // TDOR timestamp(string) - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm, yyyy-MM-ddTHH:mm:ss. All time stamps are UTC.
|
||
public $recording_time; // TDRC timestamp(string) - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm, yyyy-MM-ddTHH:mm:ss. All time stamps are UTC.
|
||
public $release_time; // TDRL timestamp(string) - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm, yyyy-MM-ddTHH:mm:ss. All time stamps are UTC.
|
||
public $tagging_time; // TDTG timestamp(string) - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm, yyyy-MM-ddTHH:mm:ss. All time stamps are UTC.
|
||
public $encoder_settings; // TSSE string
|
||
public $album_sort_order; // TSOA string
|
||
public $performer_sort_order; // TSOP string
|
||
public $title_sort_order; // TSOT string
|
||
|
||
// User defined text information frame
|
||
public $user_text; // TXXX array of ( unique_description(string) => value(string) )
|
||
|
||
// Comments
|
||
public $comment; // COMM
|
||
|
||
// URL link frames - details
|
||
public $commercial_information; // WCOM url(string)
|
||
public $copyright_information; // WCOP url(string)
|
||
public $url_file; // WOAF url(string)
|
||
public $url_artist; // WOAR url(string)
|
||
public $url_source; // WOAS url(string)
|
||
public $url_station; // WORS url(string)
|
||
public $payment; // WPAY url(string)
|
||
public $url_publisher; // WPUB url(string)
|
||
|
||
// User defined URL link frame
|
||
public $url_user; // WXXX array of ( unique_description(string) => url(string) )
|
||
|
||
|
||
// Unique file identifier
|
||
public $unique_file_identifier; // UFID
|
||
|
||
// Music CD identifier
|
||
public $music_cd_identifier; // MCDI
|
||
|
||
// Event timing codes
|
||
public $event_timing_codes; // ETCO
|
||
|
||
// MPEG location lookup table
|
||
public $mpeg_location_lookup_table; // MLLT
|
||
|
||
// Synchronised tempo codes
|
||
public $synchronised_tempo_codes; // SYTC
|
||
|
||
// Unsynchronised lyrics/text transcription
|
||
public $unsynchronised_lyrics; // USLT
|
||
|
||
// Synchronised lyrics/text
|
||
public $synchronised_lyrics; // SYLT
|
||
|
||
// Relative volume adjustment (2)
|
||
public $relative_volume_adjustment; // RVA2
|
||
|
||
// Equalisation (2)
|
||
public $equalisation; // EQU2
|
||
|
||
// Reverb
|
||
public $reverb; // RVRB
|
||
|
||
// Attached picture
|
||
public $attached_picture; // APIC
|
||
|
||
// General encapsulated object
|
||
public $general_encapsulated_object; // GEOB
|
||
|
||
// Play counter
|
||
public $play_counter; // PCNT
|
||
|
||
// Popularimeter
|
||
public $popularimeter; // POPM
|
||
|
||
// Recommended buffer size
|
||
public $recommended_buffer_size; // RBUF
|
||
|
||
// Audio encryption
|
||
public $audio_encryption; // AENC
|
||
|
||
// Linked information
|
||
public $linked_information; // LINK
|
||
|
||
// Position synchronisation frame
|
||
public $position_synchronisation; // POSS
|
||
|
||
// Terms of use frame
|
||
public $terms_of_use; // USER
|
||
|
||
// Ownership frame
|
||
public $ownership; // OWNE
|
||
|
||
// Commercial frame
|
||
public $commercial; // COMR
|
||
|
||
// Encryption method registration
|
||
public $encryption_method_registration; // ENCR
|
||
|
||
// Group identification registration
|
||
public $group_identification_registration; // GRID
|
||
|
||
// Private frame
|
||
public $private; // PRIV
|
||
|
||
// Signature frame
|
||
public $signature; // SIGN
|
||
|
||
// Seek frame
|
||
public $seek; // SEEK
|
||
|
||
// Audio seek point index
|
||
public $audio_seek_point_index; // ASPI
|
||
*/
|
||
|
||
|
||
// internal logic
|
||
protected $padded_length = 4096; // minimum length of ID3v2 tag in bytes
|
||
protected $previous_frames = array ();
|
||
|
||
const major_version = 3;
|
||
|
||
|
||
public function read() {
|
||
|
||
}
|
||
|
||
|
||
public function write() {
|
||
|
||
$engine = new getid3;
|
||
$engine->filename = $this->filename;
|
||
$engine->fp = fopen($this->filename, 'rb');
|
||
$engine->include_module('tag.id3v2');
|
||
|
||
$tag = new getid3_id3v2($engine);
|
||
$tag->Analyze();
|
||
|
||
if (!(int)@$engine->info['avdataoffset']) {
|
||
throw new getid3_exception('No audio data found.');
|
||
}
|
||
|
||
$this->padded_length = max(@$engine->info['id3v2']['headerlength'], $this->padded_length);
|
||
|
||
$tag = $this->generate_tag();
|
||
|
||
// insert-overwrite existing tag (padded to length of old tag if neccesary)
|
||
if (@$engine->info['id3v2']['headerlength'] && ($engine->info['id3v2']['headerlength'] == strlen($tag))) {
|
||
|
||
if (!$fp = fopen($this->filename, 'r+b')) {
|
||
throw new getid3_exception('Could not open '.$this->filename.' mode "r+b"');
|
||
}
|
||
fwrite($fp, $tag, strlen($tag));
|
||
fclose($fp);
|
||
}
|
||
|
||
// rewrite file - no tag present or new tag longer than old tag
|
||
else {
|
||
if (!$fp_source = @fopen($this->filename, 'rb')) {
|
||
throw new getid3_exception('Could not open '.$this->filename.' mode "rb"');
|
||
}
|
||
fseek($fp_source, $engine->info['avdataoffset'], SEEK_SET);
|
||
|
||
if (!$fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
|
||
throw new getid3_exception('Could not open '.$this->filename.'getid3tmp mode "w+b"');
|
||
}
|
||
|
||
fwrite($fp, $tag, strlen($tag));
|
||
|
||
while ($buffer = fread($fp_source, 16384)) {
|
||
fwrite($fp_temp, $buffer, strlen($buffer));
|
||
}
|
||
|
||
fclose($fp_temp);
|
||
fclose($fp_source);
|
||
|
||
$this->save_permissions();
|
||
unlink($this->filename);
|
||
rename($this->filename.'getid3tmp', $this->filename);
|
||
$this->restore_permissions();
|
||
}
|
||
|
||
clearstatcache();
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
public function remove() {
|
||
|
||
$engine = new getid3;
|
||
$engine->filename = $this->filename;
|
||
$engine->fp = fopen($this->filename, 'rb');
|
||
$engine->include_module('tag.id3v2');
|
||
|
||
$tag = new getid3_id3v2($engine);
|
||
$tag->Analyze();
|
||
|
||
if ((int)@$engine->info['avdataoffset']) {
|
||
|
||
if (!$fp_source = @fopen($this->filename, 'rb')) {
|
||
throw new getid3_exception('Could not open '.$this->filename.' mode "rb"');
|
||
}
|
||
fseek($fp_source, $engine->info['avdataoffset'], SEEK_SET);
|
||
|
||
if (!$fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
|
||
throw new getid3_exception('Could not open '.$this->filename.'getid3tmp mode "w+b"');
|
||
}
|
||
|
||
while ($buffer = fread($fp_source, 16384)) {
|
||
fwrite($fp_temp, $buffer, strlen($buffer));
|
||
}
|
||
|
||
fclose($fp_temp);
|
||
fclose($fp_source);
|
||
|
||
$this->save_permissions();
|
||
unlink($this->filename);
|
||
rename($this->filename.'getid3tmp', $this->filename);
|
||
$this->restore_permissions();
|
||
|
||
clearstatcache();
|
||
}
|
||
|
||
// success when removing non-existant tag
|
||
return true;
|
||
}
|
||
|
||
|
||
protected function generate_tag() {
|
||
|
||
$result = '';
|
||
|
||
$some_array = array (
|
||
'content_group_description' => 'TIT1',
|
||
'title' => 'TIT2',
|
||
'subtitle' => 'TIT3',
|
||
);
|
||
|
||
foreach ($some_array as $key => $frame_name) {
|
||
|
||
|
||
if ($frame_data = $this->generate_frame_data($frame_name, $this->$key)) {
|
||
|
||
$frame_length = $this->BigEndian2String(strlen($frame_data), 4, false);
|
||
$frame_flags = $this->generate_frame_flags();
|
||
}
|
||
|
||
$result .= $frame_name.$frame_length.$frame_flags.$frame_data;
|
||
}
|
||
|
||
|
||
// calc padded length of tag
|
||
while ($this->padded_length < (strlen($result) + 10)) {
|
||
$this->padded_length += 1024;
|
||
}
|
||
|
||
// pad up to $padded_length bytes if unpadded tag is shorter than $padded_length
|
||
if ($this->padded_length > (strlen($result) + 10)) {
|
||
$result .= @str_repeat("\x00", $this->padded_length - strlen($result) - 10);
|
||
}
|
||
|
||
$header = 'ID3';
|
||
$header .= chr(getid3_id3v2_write::major_version);
|
||
$header .= chr(0);
|
||
$header .= $this->generate_tag_flags();
|
||
$header .= getid3_lib::BigEndian2String(strlen($result), 4, true);
|
||
|
||
return $header.$result;
|
||
}
|
||
|
||
|
||
protected function generate_tag_flags($flags) {
|
||
|
||
// %abc00000
|
||
$flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
|
||
$flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
|
||
$flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
|
||
$flag .= '00000';
|
||
|
||
/**2.4
|
||
// %abcd0000
|
||
$flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
|
||
$flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
|
||
$flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
|
||
$flag .= (@$flags['footer'] ? '1' : '0'); // d - Footer present
|
||
$flag .= '0000';
|
||
*/
|
||
|
||
return chr(bindec($flag));
|
||
}
|
||
|
||
|
||
protected function generate_frame_flags($flags) {
|
||
|
||
// %abc00000 %ijk00000
|
||
$flag1 = (@$flags['tag_alter'] ? '1' : '0'); // a - Tag alter preservation (true == discard)
|
||
$flag1 .= (@$flags['file_alter'] ? '1' : '0'); // b - File alter preservation (true == discard)
|
||
$flag1 .= (@$flags['read_only'] ? '1' : '0'); // c - Read only (true == read only)
|
||
$flag1 .= '00000';
|
||
|
||
$flag2 = (@$flags['compression'] ? '1' : '0'); // i - Compression (true == compressed)
|
||
$flag2 .= (@$flags['encryption'] ? '1' : '0'); // j - Encryption (true == encrypted)
|
||
$flag2 .= (@$flags['grouping_identity'] ? '1' : '0'); // k - Grouping identity (true == contains group information)
|
||
$flag2 .= '00000';
|
||
|
||
/**2.4
|
||
// %0abc0000 %0h00kmnp
|
||
$flag1 = '0';
|
||
$flag1 = (@$flags['tag_alter'] ? '1' : '0'); // a - Tag alter preservation (true == discard)
|
||
$flag1 .= (@$flags['file_alter'] ? '1' : '0'); // b - File alter preservation (true == discard)
|
||
$flag1 .= (@$flags['read_only'] ? '1' : '0'); // c - Read only (true == read only)
|
||
$flag1 .= '0000';
|
||
|
||
$flag2 = '0';
|
||
$flag2 .= (@$flags['grouping_identity'] ? '1' : '0'); // h - Grouping identity (true == contains group information)
|
||
$flag2 .= '00';
|
||
$flag2 = (@$flags['compression'] ? '1' : '0'); // k - Compression (true == compressed)
|
||
$flag2 .= (@$flags['encryption'] ? '1' : '0'); // m - Encryption (true == encrypted)
|
||
$flag2 .= (@$flags['unsynchronisation'] ? '1' : '0'); // n - Unsynchronisation (true == unsynchronised)
|
||
$flag2 .= (@$flags['data_length_indicator'] ? '1' : '0'); // p - Data length indicator (true == data length indicator added)
|
||
*/
|
||
|
||
return chr(bindec($flag1)).chr(bindec($flag2));
|
||
}
|
||
|
||
|
||
protected function generate_frame_data($frame_name, $source_data_array) {
|
||
|
||
$frame_data = '';
|
||
|
||
switch ($frame_name) {
|
||
|
||
case 'UFID':
|
||
// 4.1 UFID Unique file identifier
|
||
// Owner identifier <text string> $00
|
||
// Identifier <up to 64 bytes binary data>
|
||
if (strlen($source_data_array['data']) > 64) {
|
||
throw new getid3_exception('Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)');
|
||
}
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
|
||
$frame_data .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
|
||
break;
|
||
|
||
case 'TXXX':
|
||
// 4.2.2 TXXX User defined text information frame
|
||
// Text encoding $xx
|
||
// Description <text string according to encoding> $00 (00)
|
||
// Value <text string according to encoding>
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'WXXX':
|
||
// 4.3.2 WXXX User defined URL link frame
|
||
// Text encoding $xx
|
||
// Description <text string according to encoding> $00 (00)
|
||
// URL <text string>
|
||
if (!isset($source_data_array['data']) || !$this->valid_url($source_data_array['data'], false, false)) {
|
||
throw new getid3_exception('Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'IPLS':
|
||
// 4.4 IPLS Involved people list (ID3v2.3 only)
|
||
// Text encoding $xx
|
||
// People list strings <textstrings>
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'MCDI':
|
||
// 4.4 MCDI Music CD identifier
|
||
// CD TOC <binary data>
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'ETCO':
|
||
// 4.5 ETCO Event timing codes
|
||
// Time stamp format $xx
|
||
// Where time stamp format is:
|
||
// $01 (32-bit value) MPEG frames from beginning of file
|
||
// $02 (32-bit value) milliseconds from beginning of file
|
||
// Followed by a list of key events in the following format:
|
||
// Type of event $xx
|
||
// Time stamp $xx (xx ...)
|
||
// The 'Time stamp' is set to zero if directly at the beginning of the sound
|
||
// or after the previous event. All events MUST be sorted in chronological order.
|
||
if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
|
||
throw new getid3_exception('Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')');
|
||
}
|
||
$frame_data .= chr($source_data_array['timestampformat']);
|
||
foreach ($source_data_array as $key => $val) {
|
||
if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
|
||
throw new getid3_exception('Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')');
|
||
}
|
||
if (($key != 'timestampformat') && ($key != 'flags')) {
|
||
if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
|
||
// The 'Time stamp' is set to zero if directly at the beginning of the sound
|
||
// or after the previous event. All events MUST be sorted in chronological order.
|
||
throw new getid3_exception('Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')');
|
||
}
|
||
$frame_data .= chr($val['typeid']);
|
||
$frame_data .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'MLLT':
|
||
// 4.6 MLLT MPEG location lookup table
|
||
// MPEG frames between reference $xx xx
|
||
// Bytes between reference $xx xx xx
|
||
// Milliseconds between reference $xx xx xx
|
||
// Bits for bytes deviation $xx
|
||
// Bits for milliseconds dev. $xx
|
||
// Then for every reference the following data is included;
|
||
// Deviation in bytes %xxx....
|
||
// Deviation in milliseconds %xxx....
|
||
if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
|
||
}
|
||
else {
|
||
throw new getid3_exception('Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')');
|
||
}
|
||
if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
|
||
}
|
||
else {
|
||
throw new getid3_exception('Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')');
|
||
}
|
||
if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
|
||
}
|
||
else {
|
||
throw new getid3_exception('Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
|
||
if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
|
||
$frame_data .= chr($source_data_array['bitsforbytesdeviation']);
|
||
}
|
||
else {
|
||
throw new getid3_exception('Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.');
|
||
}
|
||
}
|
||
else {
|
||
throw new getid3_exception('Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
|
||
if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
|
||
$frame_data .= chr($source_data_array['bitsformsdeviation']);
|
||
}
|
||
else {
|
||
throw new getid3_exception('Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.');
|
||
}
|
||
}
|
||
else {
|
||
throw new getid3_exception('Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')');
|
||
}
|
||
foreach ($source_data_array as $key => $val) {
|
||
if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
|
||
$unwritten_bit_stream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
|
||
$unwritten_bit_stream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT);
|
||
}
|
||
}
|
||
for ($i = 0; $i < strlen($unwritten_bit_stream); $i += 8) {
|
||
$high_nibble = bindec(substr($unwritten_bit_stream, $i, 4)) << 4;
|
||
$low_nibble = bindec(substr($unwritten_bit_stream, $i + 4, 4));
|
||
$frame_data .= chr($high_nibble & $low_nibble);
|
||
}
|
||
break;
|
||
|
||
case 'SYTC':
|
||
// 4.7 SYTC Synchronised tempo codes
|
||
// Time stamp format $xx
|
||
// Tempo data <binary data>
|
||
// Where time stamp format is:
|
||
// $01 (32-bit value) MPEG frames from beginning of file
|
||
// $02 (32-bit value) milliseconds from beginning of file
|
||
if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
|
||
throw new getid3_exception('Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')');
|
||
}
|
||
$frame_data .= chr($source_data_array['timestampformat']);
|
||
foreach ($source_data_array as $key => $val) {
|
||
if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
|
||
throw new getid3_exception('Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')');
|
||
}
|
||
if (($key != 'timestampformat') && ($key != 'flags')) {
|
||
if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
|
||
throw new getid3_exception('Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')');
|
||
}
|
||
if ($val['tempo'] > 255) {
|
||
$frame_data .= chr(255);
|
||
$val['tempo'] -= 255;
|
||
}
|
||
$frame_data .= chr($val['tempo']);
|
||
$frame_data .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'USLT':
|
||
// 4.8 USLT Unsynchronised lyric/text transcription
|
||
// Text encoding $xx
|
||
// Language $xx xx xx
|
||
// Content descriptor <text string according to encoding> $00 (00)
|
||
// Lyrics/text <full text string according to encoding>
|
||
if (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
|
||
throw new getid3_exception('Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= strtolower($source_data_array['language']);
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'SYLT':
|
||
// 4.9 SYLT Synchronised lyric/text
|
||
// Text encoding $xx
|
||
// Language $xx xx xx
|
||
// Time stamp format $xx
|
||
// $01 (32-bit value) MPEG frames from beginning of file
|
||
// $02 (32-bit value) milliseconds from beginning of file
|
||
// Content type $xx
|
||
// Content descriptor <text string according to encoding> $00 (00)
|
||
// Terminated text to be synced (typically a syllable)
|
||
// Sync identifier (terminator to above string) $00 (00)
|
||
// Time stamp $xx (xx ...)
|
||
if (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
|
||
throw new getid3_exception('Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')');
|
||
}
|
||
if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
|
||
throw new getid3_exception('Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')');
|
||
}
|
||
if (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
|
||
throw new getid3_exception('Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')');
|
||
}
|
||
if (!is_array($source_data_array['data'])) {
|
||
throw new getid3_exception('Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= strtolower($source_data_array['language']);
|
||
$frame_data .= chr($source_data_array['timestampformat']);
|
||
$frame_data .= chr($source_data_array['contenttypeid']);
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
ksort($source_data_array['data']);
|
||
foreach ($source_data_array['data'] as $key => $val) {
|
||
$frame_data .= $val['data']."\x00";
|
||
$frame_data .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
|
||
}
|
||
break;
|
||
|
||
case 'COMM':
|
||
// 4.10 COMM Comments
|
||
// Text encoding $xx
|
||
// Language $xx xx xx
|
||
// Short content descrip. <text string according to encoding> $00 (00)
|
||
// The actual text <full text string according to encoding>
|
||
if (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
|
||
throw new getid3_exception('Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= strtolower($source_data_array['language']);
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'RVA2':
|
||
// 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
|
||
// Identification <text string> $00
|
||
// The 'identification' string is used to identify the situation and/or
|
||
// device where this adjustment should apply. The following is then
|
||
// repeated for every channel:
|
||
// Type of channel $xx
|
||
// Volume adjustment $xx xx
|
||
// Bits representing peak $xx
|
||
// Peak volume $xx (xx ...)
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['description'])."\x00";
|
||
foreach ($source_data_array as $key => $val) {
|
||
if ($key != 'description') {
|
||
$frame_data .= chr($val['channeltypeid']);
|
||
$frame_data .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
|
||
if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
|
||
$frame_data .= chr($val['bitspeakvolume']);
|
||
if ($val['bitspeakvolume'] > 0) {
|
||
$frame_data .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
|
||
}
|
||
} else {
|
||
throw new getid3_exception('Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)');
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'RVAD':
|
||
// 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
|
||
// Increment/decrement %00fedcba
|
||
// Bits used for volume descr. $xx
|
||
// Relative volume change, right $xx xx (xx ...) // a
|
||
// Relative volume change, left $xx xx (xx ...) // b
|
||
// Peak volume right $xx xx (xx ...)
|
||
// Peak volume left $xx xx (xx ...)
|
||
// Relative volume change, right back $xx xx (xx ...) // c
|
||
// Relative volume change, left back $xx xx (xx ...) // d
|
||
// Peak volume right back $xx xx (xx ...)
|
||
// Peak volume left back $xx xx (xx ...)
|
||
// Relative volume change, center $xx xx (xx ...) // e
|
||
// Peak volume center $xx xx (xx ...)
|
||
// Relative volume change, bass $xx xx (xx ...) // f
|
||
// Peak volume bass $xx xx (xx ...)
|
||
if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
|
||
throw new getid3_exception('Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)');
|
||
} else {
|
||
$inc_dec_flag .= '00';
|
||
$inc_dec_flag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right
|
||
$inc_dec_flag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left
|
||
$inc_dec_flag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
|
||
$inc_dec_flag .= $source_data_array['incdec']['leftrear'] ? '1' : '0'; // d - Relative volume change, left back
|
||
$inc_dec_flag .= $source_data_array['incdec']['center'] ? '1' : '0'; // e - Relative volume change, center
|
||
$inc_dec_flag .= $source_data_array['incdec']['bass'] ? '1' : '0'; // f - Relative volume change, bass
|
||
$frame_data .= chr(bindec($inc_dec_flag));
|
||
$frame_data .= chr($source_data_array['bitsvolume']);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
|
||
if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
|
||
$source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
|
||
$source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
|
||
$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
|
||
}
|
||
if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
|
||
$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
|
||
}
|
||
if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'EQU2':
|
||
// 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
|
||
// Interpolation method $xx
|
||
// $00 Band
|
||
// $01 Linear
|
||
// Identification <text string> $00
|
||
// The following is then repeated for every adjustment point
|
||
// Frequency $xx xx
|
||
// Volume adjustment $xx xx
|
||
if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
|
||
throw new getid3_exception('Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)');
|
||
}
|
||
$frame_data .= chr($source_data_array['interpolationmethod']);
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['description'])."\x00";
|
||
foreach ($source_data_array['data'] as $key => $val) {
|
||
$frame_data .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
|
||
}
|
||
break;
|
||
|
||
case 'EQUA':
|
||
// 4.12 EQUA Equalisation (ID3v2.3 only)
|
||
// Adjustment bits $xx
|
||
// This is followed by 2 bytes + ('adjustment bits' rounded up to the
|
||
// nearest byte) for every equalisation band in the following format,
|
||
// giving a frequency range of 0 - 32767Hz:
|
||
// Increment/decrement %x (MSB of the Frequency)
|
||
// Frequency (lower 15 bits)
|
||
// Adjustment $xx (xx ...)
|
||
if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
|
||
throw new getid3_exception('Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)');
|
||
}
|
||
$frame_data .= chr($source_data_array['adjustmentbits']);
|
||
foreach ($source_data_array as $key => $val) {
|
||
if ($key != 'bitsvolume') {
|
||
if (($key > 32767) || ($key < 0)) {
|
||
throw new getid3_exception('Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)');
|
||
} else {
|
||
if ($val >= 0) {
|
||
// put MSB of frequency to 1 if increment, 0 if decrement
|
||
$key |= 0x8000;
|
||
}
|
||
$frame_data .= getid3_lib::BigEndian2String($key, 2, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'RVRB':
|
||
// 4.13 RVRB Reverb
|
||
// Reverb left (ms) $xx xx
|
||
// Reverb right (ms) $xx xx
|
||
// Reverb bounces, left $xx
|
||
// Reverb bounces, right $xx
|
||
// Reverb feedback, left to left $xx
|
||
// Reverb feedback, left to right $xx
|
||
// Reverb feedback, right to right $xx
|
||
// Reverb feedback, right to left $xx
|
||
// Premix left to right $xx
|
||
// Premix right to left $xx
|
||
if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
|
||
throw new getid3_exception('Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
|
||
throw new getid3_exception('Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
|
||
throw new getid3_exception('Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
|
||
throw new getid3_exception('Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
|
||
throw new getid3_exception('Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)');
|
||
}
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
|
||
$frame_data .= chr($source_data_array['bouncesL']);
|
||
$frame_data .= chr($source_data_array['bouncesR']);
|
||
$frame_data .= chr($source_data_array['feedbackLL']);
|
||
$frame_data .= chr($source_data_array['feedbackLR']);
|
||
$frame_data .= chr($source_data_array['feedbackRR']);
|
||
$frame_data .= chr($source_data_array['feedbackRL']);
|
||
$frame_data .= chr($source_data_array['premixLR']);
|
||
$frame_data .= chr($source_data_array['premixRL']);
|
||
break;
|
||
|
||
case 'APIC':
|
||
// 4.14 APIC Attached picture
|
||
// Text encoding $xx
|
||
// MIME type <text string> $00
|
||
// Picture type $xx
|
||
// Description <text string according to encoding> $00 (00)
|
||
// Picture data <binary data>
|
||
if (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
|
||
throw new getid3_exception('Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.getid3_id3v2_write::major_version);
|
||
}
|
||
if ((getid3_id3v2_write::major_version >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
|
||
throw new getid3_exception('Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.getid3_id3v2_write::major_version);
|
||
}
|
||
if (($source_data_array['mime'] == '-->') && (!$this->valid_url($source_data_array['data'], false, false))) {
|
||
throw new getid3_exception('Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
|
||
$frame_data .= chr($source_data_array['picturetypeid']);
|
||
$frame_data .= @$source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'GEOB':
|
||
// 4.15 GEOB General encapsulated object
|
||
// Text encoding $xx
|
||
// MIME type <text string> $00
|
||
// Filename <text string according to encoding> $00 (00)
|
||
// Content description <text string according to encoding> $00 (00)
|
||
// Encapsulated object <binary data>
|
||
if (!$this->IsValidMIMEstring($source_data_array['mime'])) {
|
||
throw new getid3_exception('Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')');
|
||
}
|
||
if (!$source_data_array['description']) {
|
||
throw new getid3_exception('Missing Description in '.$frame_name);
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
|
||
$frame_data .= $source_data_array['filename']."\x00";
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'PCNT':
|
||
// 4.16 PCNT Play counter
|
||
// When the counter reaches all one's, one byte is inserted in
|
||
// front of the counter thus making the counter eight bits bigger
|
||
// Counter $xx xx xx xx (xx ...)
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
|
||
break;
|
||
|
||
case 'POPM':
|
||
// 4.17 POPM Popularimeter
|
||
// When the counter reaches all one's, one byte is inserted in
|
||
// front of the counter thus making the counter eight bits bigger
|
||
// Email to user <text string> $00
|
||
// Rating $xx
|
||
// Counter $xx xx xx xx (xx ...)
|
||
if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
|
||
throw new getid3_exception('Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)');
|
||
}
|
||
if (!IsValidEmail($source_data_array['email'])) {
|
||
throw new getid3_exception('Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')');
|
||
}
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['email'])."\x00";
|
||
$frame_data .= chr($source_data_array['rating']);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
|
||
break;
|
||
|
||
case 'RBUF':
|
||
// 4.18 RBUF Recommended buffer size
|
||
// Buffer size $xx xx xx
|
||
// Embedded info flag %0000000x
|
||
// Offset to next tag $xx xx xx xx
|
||
if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
|
||
throw new getid3_exception('Invalid Buffer Size in '.$frame_name);
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
|
||
throw new getid3_exception('Invalid Offset To Next Tag in '.$frame_name);
|
||
}
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
|
||
$flag .= '0000000';
|
||
$flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
|
||
$frame_data .= chr(bindec($flag));
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
|
||
break;
|
||
|
||
case 'AENC':
|
||
// 4.19 AENC Audio encryption
|
||
// Owner identifier <text string> $00
|
||
// Preview start $xx xx
|
||
// Preview length $xx xx
|
||
// Encryption info <binary data>
|
||
if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
|
||
throw new getid3_exception('Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
|
||
throw new getid3_exception('Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')');
|
||
}
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
|
||
$frame_data .= $source_data_array['encryptioninfo'];
|
||
break;
|
||
|
||
case 'LINK':
|
||
// 4.20 LINK Linked information
|
||
// Frame identifier $xx xx xx xx
|
||
// URL <text string> $00
|
||
// ID and additional data <text string(s)>
|
||
if (!getid3_id3v2::valid_frame_name($source_data_array['frameid'], getid3_id3v2_write::major_version)) {
|
||
throw new getid3_exception('Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')');
|
||
}
|
||
if (!$this->valid_url($source_data_array['data'], true, false)) {
|
||
throw new getid3_exception('Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')');
|
||
}
|
||
if ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
|
||
throw new getid3_exception('Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name);
|
||
}
|
||
if (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
|
||
throw new getid3_exception('Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name);
|
||
}
|
||
if (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
|
||
throw new getid3_exception('Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name);
|
||
}
|
||
if ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
|
||
throw new getid3_exception('Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name);
|
||
}
|
||
$frame_data .= $source_data_array['frameid'];
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['data'])."\x00";
|
||
switch ($source_data_array['frameid']) {
|
||
case 'COMM':
|
||
case 'SYLT':
|
||
case 'USLT':
|
||
case 'PRIV':
|
||
case 'USER':
|
||
case 'AENC':
|
||
case 'APIC':
|
||
case 'GEOB':
|
||
case 'TXXX':
|
||
$frame_data .= $source_data_array['additionaldata'];
|
||
break;
|
||
|
||
case 'ASPI':
|
||
case 'ETCO':
|
||
case 'EQU2':
|
||
case 'MCID':
|
||
case 'MLLT':
|
||
case 'OWNE':
|
||
case 'RVA2':
|
||
case 'RVRB':
|
||
case 'SYTC':
|
||
case 'IPLS':
|
||
case 'RVAD':
|
||
case 'EQUA':
|
||
// no additional data required
|
||
break;
|
||
|
||
case 'RBUF':
|
||
if (getid3_id3v2_write::major_version == 3) {
|
||
// no additional data required
|
||
} else {
|
||
throw new getid3_exception($source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.getid3_id3v2_write::major_version.')');
|
||
}
|
||
|
||
default:
|
||
if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
|
||
// no additional data required
|
||
} else {
|
||
throw new getid3_exception($source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.getid3_id3v2_write::major_version.')');
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'POSS':
|
||
// 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
|
||
// Time stamp format $xx
|
||
// Position $xx (xx ...)
|
||
if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
|
||
throw new getid3_exception('Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
|
||
throw new getid3_exception('Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)');
|
||
}
|
||
$frame_data .= chr($source_data_array['timestampformat']);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
|
||
break;
|
||
|
||
case 'USER':
|
||
// 4.22 USER Terms of use (ID3v2.3+ only)
|
||
// Text encoding $xx
|
||
// Language $xx xx xx
|
||
// The actual text <text string according to encoding>
|
||
if (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
|
||
throw new getid3_exception('Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= strtolower($source_data_array['language']);
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'OWNE':
|
||
// 4.23 OWNE Ownership frame (ID3v2.3+ only)
|
||
// Text encoding $xx
|
||
// Price paid <text string> $00
|
||
// Date of purch. <text string>
|
||
// Seller <text string according to encoding>
|
||
if (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
|
||
throw new getid3_exception('Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')');
|
||
}
|
||
if (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
|
||
throw new getid3_exception('Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
|
||
$frame_data .= $source_data_array['purchasedate'];
|
||
$frame_data .= $source_data_array['seller'];
|
||
break;
|
||
|
||
case 'COMR':
|
||
// 4.24 COMR Commercial frame (ID3v2.3+ only)
|
||
// Text encoding $xx
|
||
// Price string <text string> $00
|
||
// Valid until <text string>
|
||
// Contact URL <text string> $00
|
||
// Received as $xx
|
||
// Name of seller <text string according to encoding> $00 (00)
|
||
// Description <text string according to encoding> $00 (00)
|
||
// Picture MIME type <string> $00
|
||
// Seller logo <binary data>
|
||
if (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
|
||
throw new getid3_exception('Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)');
|
||
}
|
||
if (!$this->valid_url($source_data_array['contacturl'], false, true)) {
|
||
throw new getid3_exception('Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)');
|
||
}
|
||
if (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
|
||
throw new getid3_exception('Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)');
|
||
}if (!$this->IsValidMIMEstring($source_data_array['mime'])) {
|
||
throw new getid3_exception('Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')');
|
||
}
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
unset($price_string);
|
||
foreach ($source_data_array['price'] as $key => $val) {
|
||
if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
|
||
$price_strings[] = $key.$val['value'];
|
||
} else {
|
||
throw new getid3_exception('Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')');
|
||
}
|
||
}
|
||
$frame_data .= implode('/', $price_strings);
|
||
$frame_data .= $source_data_array['pricevaliduntil'];
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
|
||
$frame_data .= chr($source_data_array['receivedasid']);
|
||
$frame_data .= $source_data_array['sellername']."\x00";
|
||
$frame_data .= $source_data_array['description']."\x00";
|
||
$frame_data .= $source_data_array['mime']."\x00";
|
||
$frame_data .= $source_data_array['logo'];
|
||
break;
|
||
|
||
case 'ENCR':
|
||
// 4.25 ENCR Encryption method registration (ID3v2.3+ only)
|
||
// Owner identifier <text string> $00
|
||
// Method symbol $xx
|
||
// Encryption data <binary data>
|
||
if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
|
||
throw new getid3_exception('Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)');
|
||
}
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
|
||
$frame_data .= ord($source_data_array['methodsymbol']);
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'GRID':
|
||
// 4.26 GRID Group identification registration (ID3v2.3+ only)
|
||
// Owner identifier <text string> $00
|
||
// Group symbol $xx
|
||
// Group dependent data <binary data>
|
||
if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
|
||
throw new getid3_exception('Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)');
|
||
}
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
|
||
$frame_data .= ord($source_data_array['groupsymbol']);
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'PRIV':
|
||
// 4.27 PRIV Private frame (ID3v2.3+ only)
|
||
// Owner identifier <text string> $00
|
||
// The private data <binary data>
|
||
$frame_data .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'SIGN':
|
||
// 4.28 SIGN Signature frame (ID3v2.4+ only)
|
||
// Group symbol $xx
|
||
// Signature <binary data>
|
||
if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
|
||
throw new getid3_exception('Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)');
|
||
}
|
||
$frame_data .= ord($source_data_array['groupsymbol']);
|
||
$frame_data .= $source_data_array['data'];
|
||
break;
|
||
|
||
case 'SEEK':
|
||
// 4.29 SEEK Seek frame (ID3v2.4+ only)
|
||
// Minimum offset to next tag $xx xx xx xx
|
||
if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
|
||
throw new getid3_exception('Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)');
|
||
}
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
|
||
break;
|
||
|
||
case 'ASPI':
|
||
// 4.30 ASPI Audio seek point index (ID3v2.4+ only)
|
||
// Indexed data start (S) $xx xx xx xx
|
||
// Indexed data length (L) $xx xx xx xx
|
||
// Number of index points (N) $xx xx
|
||
// Bits per index point (b) $xx
|
||
// Then for every index point the following data is included:
|
||
// Fraction at index (Fi) $xx (xx)
|
||
if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
|
||
throw new getid3_exception('Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
|
||
throw new getid3_exception('Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
|
||
throw new getid3_exception('Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)');
|
||
}
|
||
if (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
|
||
throw new getid3_exception('Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)');
|
||
}
|
||
if ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
|
||
throw new getid3_exception('Number Of Index Points does not match actual supplied data in '.$frame_name);
|
||
}
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
|
||
$frame_data .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
|
||
foreach ($source_data_array['indexes'] as $key => $val) {
|
||
$frame_data .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
|
||
}
|
||
break;
|
||
|
||
case 'RGAD':
|
||
// RGAD Replay Gain Adjustment
|
||
// http://privatewww.essex.ac.uk/~djmrob/replaygain/
|
||
// Peak Amplitude $xx $xx $xx $xx
|
||
// Radio Replay Gain Adjustment %aaabbbcd %dddddddd
|
||
// Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
|
||
// a - name code
|
||
// b - originator code
|
||
// c - sign bit
|
||
// d - replay gain adjustment
|
||
|
||
if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
|
||
throw new getid3_exception('Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)');
|
||
}
|
||
if (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
|
||
throw new getid3_exception('Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)');
|
||
}
|
||
if (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
|
||
throw new getid3_exception('Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)');
|
||
}
|
||
if (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
|
||
throw new getid3_exception('Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)');
|
||
}
|
||
if (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
|
||
throw new getid3_exception('Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)');
|
||
}
|
||
if (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
|
||
throw new getid3_exception('Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)');
|
||
}
|
||
$frame_data .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
|
||
$frame_data .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
|
||
$frame_data .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
|
||
break;
|
||
|
||
default:
|
||
|
||
if ($frame_name{0} == 'T') {
|
||
// 4.2. T??? Text information frames
|
||
// Text encoding $xx
|
||
// Information <text string(s) according to encoding>
|
||
$frame_data .= chr(3); // UTF-8 encoding
|
||
$frame_data .= $source_data_array['data'];
|
||
}
|
||
|
||
elseif ($frame_name{0} == 'W') {
|
||
// 4.3. W??? URL link frames
|
||
// URL <text string>
|
||
if (!$this->valid_url($source_data_array['data'], false, false)) {
|
||
throw new getid3_exception('Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')');
|
||
} else {
|
||
$frame_data .= $source_data_array['data'];
|
||
}
|
||
} else {
|
||
throw new getid3_exception($frame_name.' not supported by generate_frame_data()');
|
||
}
|
||
break;
|
||
}
|
||
|
||
return $frame_data;
|
||
}
|
||
|
||
|
||
protected function frame_allowed($frame_name, $source_data_array) {
|
||
|
||
if (getid3_id3v2_write::major_version == 4) {
|
||
switch ($frame_name) {
|
||
case 'UFID':
|
||
case 'AENC':
|
||
case 'ENCR':
|
||
case 'GRID':
|
||
if (!isset($source_data_array['ownerid'])) {
|
||
throw new getid3_exception('[ownerid] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['ownerid'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['ownerid'];
|
||
break;
|
||
|
||
case 'TXXX':
|
||
case 'WXXX':
|
||
case 'RVA2':
|
||
case 'EQU2':
|
||
case 'APIC':
|
||
case 'GEOB':
|
||
if (!isset($source_data_array['description'])) {
|
||
throw new getid3_exception('[description] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['description'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['description'];
|
||
break;
|
||
|
||
case 'USER':
|
||
if (!isset($source_data_array['language'])) {
|
||
throw new getid3_exception('[language] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['language'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['language'];
|
||
break;
|
||
|
||
case 'USLT':
|
||
case 'SYLT':
|
||
case 'COMM':
|
||
if (!isset($source_data_array['language'])) {
|
||
throw new getid3_exception('[language] not specified for '.$frame_name);
|
||
}
|
||
if (!isset($source_data_array['description'])) {
|
||
throw new getid3_exception('[description] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
|
||
break;
|
||
|
||
case 'POPM':
|
||
if (!isset($source_data_array['email'])) {
|
||
throw new getid3_exception('[email] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['email'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['email'];
|
||
break;
|
||
|
||
case 'IPLS':
|
||
case 'MCDI':
|
||
case 'ETCO':
|
||
case 'MLLT':
|
||
case 'SYTC':
|
||
case 'RVRB':
|
||
case 'PCNT':
|
||
case 'RBUF':
|
||
case 'POSS':
|
||
case 'OWNE':
|
||
case 'SEEK':
|
||
case 'ASPI':
|
||
case 'RGAD':
|
||
if (in_array($frame_name, $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed');
|
||
}
|
||
$this->previous_frames[] = $frame_name;
|
||
break;
|
||
|
||
case 'LINK':
|
||
// this isn't implemented quite right (yet) - it should check the target frame data for compliance
|
||
// but right now it just allows one linked frame of each type, to be safe.
|
||
if (!isset($source_data_array['frameid'])) {
|
||
throw new getid3_exception('[frameid] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['frameid'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')');
|
||
}
|
||
if (in_array($source_data_array['frameid'], $this->previous_frames)) {
|
||
// no links to singleton tags
|
||
throw new getid3_exception('Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
|
||
$this->previous_frames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type
|
||
break;
|
||
|
||
case 'COMR':
|
||
// There may be more than one 'commercial frame' in a tag, but no two may be identical
|
||
// Checking isn't implemented at all (yet) - just assumes that it's OK.
|
||
break;
|
||
|
||
case 'PRIV':
|
||
case 'SIGN':
|
||
if (!isset($source_data_array['ownerid'])) {
|
||
throw new getid3_exception('[ownerid] not specified for '.$frame_name);
|
||
}
|
||
if (!isset($source_data_array['data'])) {
|
||
throw new getid3_exception('[data] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
|
||
break;
|
||
|
||
default:
|
||
if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
|
||
throw new getid3_exception('Frame not allowed in ID3v2.'.getid3_id3v2_write::major_version.': '.$frame_name);
|
||
}
|
||
break;
|
||
}
|
||
|
||
} elseif (getid3_id3v2_write::major_version == 3) {
|
||
|
||
switch ($frame_name) {
|
||
case 'UFID':
|
||
case 'AENC':
|
||
case 'ENCR':
|
||
case 'GRID':
|
||
if (!isset($source_data_array['ownerid'])) {
|
||
throw new getid3_exception('[ownerid] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['ownerid'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['ownerid'];
|
||
break;
|
||
|
||
case 'TXXX':
|
||
case 'WXXX':
|
||
case 'APIC':
|
||
case 'GEOB':
|
||
if (!isset($source_data_array['description'])) {
|
||
throw new getid3_exception('[description] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['description'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['description'];
|
||
break;
|
||
|
||
case 'USER':
|
||
if (!isset($source_data_array['language'])) {
|
||
throw new getid3_exception('[language] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['language'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['language'];
|
||
break;
|
||
|
||
case 'USLT':
|
||
case 'SYLT':
|
||
case 'COMM':
|
||
if (!isset($source_data_array['language'])) {
|
||
throw new getid3_exception('[language] not specified for '.$frame_name);
|
||
}
|
||
if (!isset($source_data_array['description'])) {
|
||
throw new getid3_exception('[description] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
|
||
break;
|
||
|
||
case 'POPM':
|
||
if (!isset($source_data_array['email'])) {
|
||
throw new getid3_exception('[email] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['email'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['email'];
|
||
break;
|
||
|
||
case 'IPLS':
|
||
case 'MCDI':
|
||
case 'ETCO':
|
||
case 'MLLT':
|
||
case 'SYTC':
|
||
case 'RVAD':
|
||
case 'EQUA':
|
||
case 'RVRB':
|
||
case 'PCNT':
|
||
case 'RBUF':
|
||
case 'POSS':
|
||
case 'OWNE':
|
||
case 'RGAD':
|
||
if (in_array($frame_name, $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed');
|
||
}
|
||
$this->previous_frames[] = $frame_name;
|
||
break;
|
||
|
||
case 'LINK':
|
||
// this isn't implemented quite right (yet) - it should check the target frame data for compliance
|
||
// but right now it just allows one linked frame of each type, to be safe.
|
||
if (!isset($source_data_array['frameid'])) {
|
||
throw new getid3_exception('[frameid] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['frameid'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')');
|
||
}
|
||
if (in_array($source_data_array['frameid'], $this->previous_frames)) {
|
||
// no links to singleton tags
|
||
throw new getid3_exception('Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
|
||
$this->previous_frames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type
|
||
break;
|
||
|
||
case 'COMR':
|
||
// There may be more than one 'commercial frame' in a tag, but no two may be identical
|
||
// Checking isn't implemented at all (yet) - just assumes that it's OK.
|
||
break;
|
||
|
||
case 'PRIV':
|
||
if (!isset($source_data_array['ownerid'])) {
|
||
throw new getid3_exception('[ownerid] not specified for '.$frame_name);
|
||
}
|
||
if (!isset($source_data_array['data'])) {
|
||
throw new getid3_exception('[data] not specified for '.$frame_name);
|
||
}
|
||
if (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $this->previous_frames)) {
|
||
throw new getid3_exception('Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')');
|
||
}
|
||
$this->previous_frames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
|
||
break;
|
||
|
||
default:
|
||
if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
|
||
throw new getid3_exception('Frame not allowed in ID3v2.'.getid3_id3v2_write::major_version.': '.$frame_name);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidPriceString($price_string) {
|
||
|
||
if (getid3_id3v2::LanguageLookup(substr($price_string, 0, 3), true) == '') {
|
||
return false;
|
||
} elseif (!$this->IsANumber(substr($price_string, 3), true)) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidETCOevent($event_id) {
|
||
|
||
if (($event_id < 0) || ($event_id > 0xFF)) {
|
||
// outside range of 1 byte
|
||
return false;
|
||
} elseif (($event_id >= 0xF0) && ($event_id <= 0xFC)) {
|
||
// reserved for future use
|
||
return false;
|
||
} elseif (($event_id >= 0x17) && ($event_id <= 0xDF)) {
|
||
// reserved for future use
|
||
return false;
|
||
} elseif (($event_id >= 0x0E) && ($event_id <= 0x16) && (getid3_id3v2_write::major_version == 2)) {
|
||
// not defined in ID3v2.2
|
||
return false;
|
||
} elseif (($event_id >= 0x15) && ($event_id <= 0x16) && (getid3_id3v2_write::major_version == 3)) {
|
||
// not defined in ID3v2.3
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidSYLTtype($content_type) {
|
||
if (($content_type >= 0) && ($content_type <= 8) && (getid3_id3v2_write::major_version == 4)) {
|
||
return true;
|
||
} elseif (($content_type >= 0) && ($content_type <= 6) && (getid3_id3v2_write::major_version == 3)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidRVA2channeltype($channel_type) {
|
||
|
||
if (($channel_type >= 0) && ($channel_type <= 8) && (getid3_id3v2_write::major_version == 4)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidAPICpicturetype($picture_type) {
|
||
|
||
if (($picture_type >= 0) && ($picture_type <= 0x14) && (getid3_id3v2_write::major_version >= 2) && (getid3_id3v2_write::major_version <= 4)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidAPICimageformat($image_format) {
|
||
|
||
if ($image_format == '-->') {
|
||
return true;
|
||
} elseif (getid3_id3v2_write::major_version == 2) {
|
||
if ((strlen($image_format) == 3) && ($image_format == strtoupper($image_format))) {
|
||
return true;
|
||
}
|
||
} elseif ((getid3_id3v2_write::major_version == 3) || (getid3_id3v2_write::major_version == 4)) {
|
||
if ($this->IsValidMIMEstring($image_format)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidCOMRreceivedAs($received_as) {
|
||
|
||
if ((getid3_id3v2_write::major_version >= 3) && ($received_as >= 0) && ($received_as <= 8)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidRGADname($rgad_name) {
|
||
|
||
if (($rgad_name >= 0) && ($rgad_name <= 2)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function ID3v2IsValidRGADoriginator($rgad_originator) {
|
||
|
||
if (($rgad_originator >= 0) && ($rgad_originator <= 3)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function is_hash($var) {
|
||
|
||
// written by dev-null<6C>christophe*vg
|
||
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
|
||
if (is_array($var)) {
|
||
$keys = array_keys($var);
|
||
$all_num = true;
|
||
for ($i = 0; $i < count($keys); $i++) {
|
||
if (is_string($keys[$i])) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function IsValidMIMEstring($mime_string) {
|
||
|
||
if ((strlen($mime_string) >= 3) && (strpos($mime_string, '/') > 0) && (strpos($mime_string, '/') < (strlen($mime_string) - 1))) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function IsWithinBitRange($number, $max_bits, $signed=false) {
|
||
|
||
if ($signed) {
|
||
if (($number > (0 - pow(2, $max_bits - 1))) && ($number <= pow(2, $max_bits - 1))) {
|
||
return true;
|
||
}
|
||
} else {
|
||
if (($number >= 0) && ($number <= pow(2, $max_bits))) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
static public function safe_parse_url($url) {
|
||
|
||
$parts = @parse_url($url);
|
||
$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
|
||
$parts['host'] = (isset($parts['host']) ? $parts['host'] : '');
|
||
$parts['user'] = (isset($parts['user']) ? $parts['user'] : '');
|
||
$parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : '');
|
||
$parts['path'] = (isset($parts['path']) ? $parts['path'] : '');
|
||
$parts['query'] = (isset($parts['query']) ? $parts['query'] : '');
|
||
return $parts;
|
||
}
|
||
|
||
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
//// // probably should be an error, need to rewrite valid_url() to handle other encodings
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
///////////////////////
|
||
|
||
static public function valid_url($url, $allow_user_pass=false) {
|
||
|
||
if ($url == '') {
|
||
return false;
|
||
}
|
||
if ($allow_user_pass !== true) {
|
||
if (strstr($url, '@')) {
|
||
// in the format http://user:pass@example.com or http://user@example.com
|
||
// but could easily be somebody incorrectly entering an email address in place of a URL
|
||
return false;
|
||
}
|
||
}
|
||
if ($parts = $this->safe_parse_url($url)) {
|
||
if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
|
||
return false;
|
||
} elseif (!preg_match("^/[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$/", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
|
||
return false;
|
||
} elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/", $parts['user'], $regs)) {
|
||
return false;
|
||
} elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/", $parts['pass'], $regs)) {
|
||
return false;
|
||
} elseif (!preg_match("/^[[:alnum:]\/_\.@~-]*$/", $parts['path'], $regs)) {
|
||
return false;
|
||
} elseif (!preg_match("^/[[:alnum:]?&=+:;_()%#\/,\.-]*$/", $parts['query'], $regs)) {
|
||
return false;
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
public static function BigEndian2String($number, $min_bytes=1, $synch_safe=false, $signed=false) {
|
||
|
||
if ($number < 0) {
|
||
return false;
|
||
}
|
||
|
||
$maskbyte = (($synch_safe || $signed) ? 0x7F : 0xFF);
|
||
|
||
$intstring = '';
|
||
|
||
if ($signed) {
|
||
if ($min_bytes > 4) {
|
||
die('INTERNAL ERROR: Cannot have signed integers larger than 32-bits in BigEndian2String()');
|
||
}
|
||
$number = $number & (0x80 << (8 * ($min_bytes - 1)));
|
||
}
|
||
|
||
while ($number != 0) {
|
||
$quotient = ($number / ($maskbyte + 1));
|
||
$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
|
||
$number = floor($quotient);
|
||
}
|
||
return str_pad($intstring, $min_bytes, "\x00", STR_PAD_LEFT);
|
||
}
|
||
}
|
||
|
||
|
||
?>
|