├── LICENSE
├── README
├── getid3
├── extension.cache.dbm.php
├── extension.cache.mysql.php
├── getid3.lib.php
├── getid3.php
├── module.archive.rar.php
├── module.archive.szip.php
├── module.archive.zip.php
├── module.audio-video.asf.php
├── module.audio-video.bink.php
├── module.audio-video.matroska.php
├── module.audio-video.mpeg.php
├── module.audio-video.nsv.php
├── module.audio-video.quicktime.php
├── module.audio-video.real.php
├── module.audio-video.riff.php
├── module.audio-video.swf.php
├── module.audio.aac.php
├── module.audio.ac3.php
├── module.audio.au.php
├── module.audio.avr.php
├── module.audio.bonk.php
├── module.audio.flac.php
├── module.audio.la.php
├── module.audio.lpac.php
├── module.audio.midi.php
├── module.audio.mod.php
├── module.audio.monkey.php
├── module.audio.mp3.php
├── module.audio.mpc.php
├── module.audio.ogg.php
├── module.audio.optimfrog.php
├── module.audio.rkau.php
├── module.audio.shorten.php
├── module.audio.tta.php
├── module.audio.voc.php
├── module.audio.vqf.php
├── module.audio.wavpack.php
├── module.graphic.bmp.php
├── module.graphic.gif.php
├── module.graphic.jpg.php
├── module.graphic.pcd.php
├── module.graphic.png.php
├── module.graphic.tiff.php
├── module.misc.exe.php
├── module.misc.iso.php
├── module.tag.apetag.php
├── module.tag.id3v1.php
├── module.tag.id3v2.php
├── module.tag.lyrics3.php
├── write.apetag.php
├── write.id3v1.php
├── write.id3v2.php
├── write.lyrics3.php
├── write.metaflac.php
├── write.php
├── write.real.php
└── write.vorbiscomment.php
├── index.php
└── music
├── Boston Pops-John Williams - Super Mario Brothers Theme.mp3
├── Debussy - Clair de Lune.mp3
└── Dido - White Flag.mp3
/LICENSE:
--------------------------------------------------------------------------------
1 | License
2 | =======
3 |
4 | Public domain. Do whatever you see fit with the source. No warrenty of course.
5 |
6 | Please let me know what you've been up to. I hold the right to reference to your project.
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | _______ __ __ _______
2 | | __| |_.----..-----..---.-..--------.|__|.-----..-----. | __|.-----..----..--.--..-----..----*
3 | |__ | _| _|| -__|| _ || || || || _ | |__ || -__|| _|| | || -__|| _|
4 | |_______|____|__| |_____||___._||__|__|__||__||__|__||___ | |_______||_____||__| \___/ |_____||__|
5 | |_____|
6 | Description
7 | ===========
8 | Autonomous pseudo streaming audio server. It supports the shoutcast protocol for mainstream players. This will output streaming mpeg for all other players.
9 |
10 | The script does not accept a single audio source, but uses lose audio files instead. It employs a trick to keep all the listeners in sync. Through a shuffled playlist with a fixed seed value, the script will always serve the same audio at the same time.
11 |
12 | The advantage is low processor usage, because the audio has not to be transcoded on the fly.
13 |
14 | Shoutcast protocol
15 | ==================
16 | The protocol is a bit of a hack. It's quite simple but there is almost no documentation on the interwebs. The protocol consists of two part: the mpeg audio and the metadata.
17 |
18 | Audio stream
19 | ------------
20 | The stream is just a simple dump of concatenated MP3 files. To make it a bit more of a 'protocol' the header and the id3 tags are stripped of the file. So only the audiodata of a MP3 file used.
21 |
22 | The metadata
23 | ------------
24 | Was a bit of a challege to reverse engineer. This was badly documented.
25 |
26 | The raw audio stream (without headers and id3-tags) is spliced into chuncks the size of the buffer. The size is specified in the icy-metaint variable in the http header. Then the metadata is inserted at these point in a certain format. The metadata must have this payload:
27 |
28 | StreamTitle='Artist - Song';
29 |
30 | and has an null-character (0x00) to end the string. The string must be padded to the next 16th position. So the example has 28 character + 1 null-character. Makes: 29 characters. You'll have to append 3 extra null-characters.
31 |
32 | Setup
33 | =====
34 | Your audio must be transcoded to MP3 in your transmission bitrate. Please normalize and trim your audio to avoid silent parts. Put the audio in the music folder.
35 |
36 | music.db
37 | --------
38 | If the music.db is present it must be deleted after every change. This is a cache-file for the metadata of the audio.
39 |
40 | Config
41 | ======
42 |
43 | Edit the settings in the index.php
44 |
45 | This is an example:
46 |
47 | $settings = array(
48 | "name" => "Radio Demo", //Name of your radio station.
49 | "genre" => "Classic", //Does not have to be a MP3 genre, can be anything.
50 | "url" => $_SERVER["SCRIPT_URI"], //URL to the station, this is automatic generated by PHP.
51 | "bitrate" => 96, //Bitrate in kbps of the transmission. All audio but be transcoded to this bitrate.
52 | "music_directory" => "music/", //Folder where the audio is.
53 | "database_file" => "music.db", //Cache filename of the audio metadata.
54 | "buffer_size" => 16384, //Buffersize of the icy-data, not really important. Bigger buffer is less updates of the current song name.
55 | "max_listen_time" => 14400, //Maximum listen time of a user in seconds. Set to 4 hours.
56 | "randomize_seed" => 31337 //The seed of the pseudo random playlist. Must be set to a contant otherwise the clients won't be in sync.
57 | );
58 |
59 | Usage
60 | =====
61 | Point your audio player to the URL where you have installed this script.
62 |
63 | Recommended players:
64 | ====================
65 | VLC - http://www.videolan.org/vlc/
66 | iTunes - http://www.apple.nl/itunes/
67 | Winamp - http://www.winamp.com/
68 |
69 | Contact
70 | =======
71 | Name: david
72 | Domain: beople.com
--------------------------------------------------------------------------------
/getid3/extension.cache.dbm.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/extension.cache.dbm.php
--------------------------------------------------------------------------------
/getid3/extension.cache.mysql.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/extension.cache.mysql.php
--------------------------------------------------------------------------------
/getid3/getid3.lib.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/getid3.lib.php
--------------------------------------------------------------------------------
/getid3/module.archive.rar.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.archive.rar.php //
11 | // module for analyzing RAR files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_rar
18 | {
19 |
20 | function getid3_rar(&$fd, &$ThisFileInfo) {
21 |
22 | $ThisFileInfo['fileformat'] = 'rar';
23 |
24 | $ThisFileInfo['error'][] = 'RAR parsing not enabled in this version of getID3()';
25 | return false;
26 |
27 | }
28 |
29 | }
30 |
31 |
32 | ?>
--------------------------------------------------------------------------------
/getid3/module.archive.szip.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.archive.szip.php //
11 | // module for analyzing SZIP compressed files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_szip
18 | {
19 |
20 | function getid3_szip(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $SZIPHeader = fread($fd, 6);
24 | if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") {
25 | $ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"';
26 | return false;
27 | }
28 |
29 | $ThisFileInfo['fileformat'] = 'szip';
30 |
31 | $ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
32 | $ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
33 |
34 | while (!feof($fd)) {
35 | $NextBlockID = fread($fd, 2);
36 | switch ($NextBlockID) {
37 | case 'SZ':
38 | // Note that szip files can be concatenated, this has the same effect as
39 | // concatenating the files. this also means that global header blocks
40 | // might be present between directory/data blocks.
41 | fseek($fd, 4, SEEK_CUR);
42 | break;
43 |
44 | case 'BH':
45 | $BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3));
46 | $BHheaderdata = fread($fd, $BHheaderbytes);
47 | $BHheaderoffset = 0;
48 | while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
49 | //filename as \0 terminated string (empty string indicates end)
50 | //owner as \0 terminated string (empty is same as last file)
51 | //group as \0 terminated string (empty is same as last file)
52 | //3 byte filelength in this block
53 | //2 byte access flags
54 | //4 byte creation time (like in unix)
55 | //4 byte modification time (like in unix)
56 | //4 byte access time (like in unix)
57 |
58 | $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
59 | $BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
60 |
61 | $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
62 | $BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
63 |
64 | $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
65 | $BHheaderoffset += (strlen($BHdataArray['group']) + 1);
66 |
67 | $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
68 | $BHheaderoffset += 3;
69 |
70 | $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
71 | $BHheaderoffset += 2;
72 |
73 | $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
74 | $BHheaderoffset += 4;
75 |
76 | $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
77 | $BHheaderoffset += 4;
78 |
79 | $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
80 | $BHheaderoffset += 4;
81 |
82 | $ThisFileInfo['szip']['BH'][] = $BHdataArray;
83 | }
84 | break;
85 |
86 | default:
87 | break 2;
88 | }
89 | }
90 |
91 | return true;
92 |
93 | }
94 |
95 | }
96 |
97 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio-video.bink.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.bink.php //
11 | // module for analyzing Bink or Smacker audio-video files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_bink
18 | {
19 |
20 | function getid3_bink(&$fd, &$ThisFileInfo) {
21 |
22 | $ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()';
23 |
24 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
25 | $fileTypeID = fread($fd, 3);
26 | switch ($fileTypeID) {
27 | case 'BIK':
28 | return $this->ParseBink($fd, $ThisFileInfo);
29 | break;
30 |
31 | case 'SMK':
32 | return $this->ParseSmacker($fd, $ThisFileInfo);
33 | break;
34 |
35 | default:
36 | $ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"';
37 | return false;
38 | break;
39 | }
40 |
41 | return true;
42 |
43 | }
44 |
45 | function ParseBink(&$fd, &$ThisFileInfo) {
46 | $ThisFileInfo['fileformat'] = 'bink';
47 | $ThisFileInfo['video']['dataformat'] = 'bink';
48 |
49 | $fileData = 'BIK'.fread($fd, 13);
50 |
51 | $ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
52 | $ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
53 |
54 | if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) {
55 | $ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
56 | }
57 |
58 | return true;
59 | }
60 |
61 | function ParseSmacker(&$fd, &$ThisFileInfo) {
62 | $ThisFileInfo['fileformat'] = 'smacker';
63 | $ThisFileInfo['video']['dataformat'] = 'smacker';
64 |
65 | return false;
66 | }
67 |
68 | }
69 |
70 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio-video.matroska.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio-video.matriska.php //
11 | // module for analyzing Matroska containers //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_matroska
18 | {
19 |
20 | function getid3_matroska(&$fd, &$ThisFileInfo) {
21 |
22 | $ThisFileInfo['fileformat'] = 'matroska';
23 |
24 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
25 |
26 | //$ThisFileInfo['matroska']['raw']['a'] = $this->EBML2Int(fread($fd, 4));
27 |
28 | $ThisFileInfo['error'][] = 'Mastroka parsing not enabled in this version of getID3()';
29 | return false;
30 |
31 | }
32 |
33 |
34 | function EBML2Int($EBMLstring) {
35 | // http://matroska.org/specs/
36 |
37 | // Element ID coded with an UTF-8 like system:
38 | // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X)
39 | // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
40 | // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
41 | // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
42 | // Values with all x at 0 and 1 are reserved (hence the -2).
43 |
44 | // Data size, in octets, is also coded with an UTF-8 like system :
45 | // 1xxx xxxx - value 0 to 2^7-2
46 | // 01xx xxxx xxxx xxxx - value 0 to 2^14-2
47 | // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2
48 | // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2
49 | // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2
50 | // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2
51 | // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2
52 | // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2
53 |
54 | if (0x80 & ord($EBMLstring{0})) {
55 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x7F);
56 | } elseif (0x40 & ord($EBMLstring{0})) {
57 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x3F);
58 | } elseif (0x20 & ord($EBMLstring{0})) {
59 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x1F);
60 | } elseif (0x10 & ord($EBMLstring{0})) {
61 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x0F);
62 | } elseif (0x08 & ord($EBMLstring{0})) {
63 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x07);
64 | } elseif (0x04 & ord($EBMLstring{0})) {
65 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x03);
66 | } elseif (0x02 & ord($EBMLstring{0})) {
67 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x01);
68 | } elseif (0x01 & ord($EBMLstring{0})) {
69 | $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x00);
70 | } else {
71 | return false;
72 | }
73 | return getid3_lib::BigEndian2Int($EBMLstring);
74 | }
75 |
76 | }
77 |
78 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio-video.mpeg.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio-video.mpeg.php //
11 | // module for analyzing MPEG files //
12 | // dependencies: module.audio.mp3.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
17 |
18 | define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
19 | define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
20 | define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
21 | define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4");
22 | define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
23 | define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7");
24 | define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8");
25 | define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0");
26 |
27 |
28 | class getid3_mpeg
29 | {
30 |
31 | function getid3_mpeg(&$fd, &$ThisFileInfo) {
32 | if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) {
33 | $ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')';
34 | return false;
35 | }
36 | $ThisFileInfo['fileformat'] = 'mpeg';
37 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
38 | $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']));
39 | $MPEGstreamDataLength = strlen($MPEGstreamData);
40 |
41 | $foundVideo = true;
42 | $VideoChunkOffset = 0;
43 | while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
44 | if ($VideoChunkOffset >= $MPEGstreamDataLength) {
45 | $foundVideo = false;
46 | break;
47 | }
48 | }
49 | if ($foundVideo) {
50 |
51 | // Start code 32 bits
52 | // horizontal frame size 12 bits
53 | // vertical frame size 12 bits
54 | // pixel aspect ratio 4 bits
55 | // frame rate 4 bits
56 | // bitrate 18 bits
57 | // marker bit 1 bit
58 | // VBV buffer size 10 bits
59 | // constrained parameter flag 1 bit
60 | // intra quant. matrix flag 1 bit
61 | // intra quant. matrix values 512 bits (present if matrix flag == 1)
62 | // non-intra quant. matrix flag 1 bit
63 | // non-intra quant. matrix values 512 bits (present if matrix flag == 1)
64 |
65 | $ThisFileInfo['video']['dataformat'] = 'mpeg';
66 |
67 | $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
68 |
69 | $FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
70 | $VideoChunkOffset += 3;
71 |
72 | $AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
73 | $VideoChunkOffset += 1;
74 |
75 | $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
76 | $VideoChunkOffset += 4;
77 |
78 | $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
79 | $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size
80 | $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
81 | $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F);
82 |
83 | $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'];
84 | $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'];
85 |
86 | $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
87 | $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
88 | $ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']);
89 |
90 | $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18));
91 | $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1));
92 | $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10));
93 | $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1));
94 | $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1));
95 | if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) {
96 |
97 | // read 512 bits
98 | $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
99 | $VideoChunkOffset += 64;
100 |
101 | $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1));
102 | $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
103 |
104 | if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
105 | $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
106 | $VideoChunkOffset += 64;
107 | }
108 |
109 | } else {
110 |
111 | $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1));
112 | if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
113 | $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
114 | $VideoChunkOffset += 64;
115 | }
116 |
117 | }
118 |
119 | if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
120 |
121 | $ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files';
122 | $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr';
123 |
124 | } else {
125 |
126 | $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400;
127 | $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr';
128 | $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate'];
129 |
130 | }
131 |
132 | $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal'];
133 | $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical'];
134 | $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate'];
135 | $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode'];
136 | $ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'];
137 | $ThisFileInfo['video']['lossless'] = false;
138 | $ThisFileInfo['video']['bits_per_sample'] = 24;
139 |
140 | } else {
141 |
142 | $ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
143 |
144 | }
145 |
146 | //0x000001B3 begins the sequence_header of every MPEG video stream.
147 | //But in MPEG-2, this header must immediately be followed by an
148 | //extension_start_code (0x000001B5) with a sequence_extension ID (1).
149 | //(This extension contains all the additional MPEG-2 stuff.)
150 | //MPEG-1 doesn't have this extension, so that's a sure way to tell the
151 | //difference between MPEG-1 and MPEG-2 video streams.
152 |
153 | if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
154 | $ThisFileInfo['video']['codec'] = 'MPEG-2';
155 | } else {
156 | $ThisFileInfo['video']['codec'] = 'MPEG-1';
157 | }
158 |
159 |
160 | $AudioChunkOffset = 0;
161 | while (true) {
162 | while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
163 | if ($AudioChunkOffset >= $MPEGstreamDataLength) {
164 | break 2;
165 | }
166 | }
167 |
168 | for ($i = 0; $i <= 7; $i++) {
169 | // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
170 | // I have no idea why or what the difference is, so this is a stupid hack.
171 | // If anybody has any better idea of what's going on, please let me know - info@getid3.org
172 |
173 | $dummy = $ThisFileInfo;
174 | if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) {
175 | $ThisFileInfo = $dummy;
176 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
177 | $ThisFileInfo['audio']['lossless'] = false;
178 | break 2;
179 |
180 | }
181 | }
182 | }
183 |
184 | // Temporary hack to account for interleaving overhead:
185 | if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) {
186 | $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']);
187 |
188 | // Interleaved MPEG audio/video files have a certain amount of overhead that varies
189 | // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
190 | // Use interpolated lookup tables to approximately guess how much is overhead, because
191 | // playtime is calculated as filesize / total-bitrate
192 | $ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']);
193 |
194 | //switch ($ThisFileInfo['video']['bitrate']) {
195 | // case('5000000'):
196 | // $multiplier = 0.93292642112380355828048824319889;
197 | // break;
198 | // case('5500000'):
199 | // $multiplier = 0.93582895375200989965359777343219;
200 | // break;
201 | // case('6000000'):
202 | // $multiplier = 0.93796247714820932532911373859139;
203 | // break;
204 | // case('7000000'):
205 | // $multiplier = 0.9413264083635103463010117778776;
206 | // break;
207 | // default:
208 | // $multiplier = 1;
209 | // break;
210 | //}
211 | //$ThisFileInfo['playtime_seconds'] *= $multiplier;
212 | //$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
213 | if ($ThisFileInfo['video']['bitrate'] < 50000) {
214 | $ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
215 | }
216 | }
217 |
218 | return true;
219 | }
220 |
221 |
222 | function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
223 | $OverheadPercentage = 0;
224 |
225 | $AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
226 | $VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
227 |
228 |
229 | //OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
230 | $OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
231 | $OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
232 | $OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
233 | $OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
234 | $OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
235 | $OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
236 | $OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
237 | $OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
238 | $OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
239 | $OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
240 | $OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
241 | $OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
242 |
243 | $BitrateToUseMin = 32;
244 | $BitrateToUseMax = 32;
245 | $previousBitrate = 32;
246 | foreach ($OverheadMultiplierByBitrate as $key => $value) {
247 | if ($AudioBitrate >= $previousBitrate) {
248 | $BitrateToUseMin = $previousBitrate;
249 | }
250 | if ($AudioBitrate < $key) {
251 | $BitrateToUseMax = $key;
252 | break;
253 | }
254 | $previousBitrate = $key;
255 | }
256 | $FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
257 |
258 | $VideoBitrateLog10 = log10($VideoBitrate);
259 | $VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
260 | $VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
261 | $VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
262 | $VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
263 | $FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
264 |
265 | $OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
266 | $OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
267 | $OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
268 | $OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
269 |
270 | return $OverheadPercentage;
271 | }
272 |
273 |
274 | function MPEGvideoFramerateLookup($rawframerate) {
275 | $MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
276 | return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
277 | }
278 |
279 | function MPEGvideoAspectRatioLookup($rawaspectratio) {
280 | $MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
281 | return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
282 | }
283 |
284 | function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
285 | $MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
286 | return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
287 | }
288 |
289 | }
290 |
291 |
292 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio-video.nsv.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.nsv.php //
11 | // module for analyzing Nullsoft NSV files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_nsv
18 | {
19 |
20 | function getid3_nsv(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $NSVheader = fread($fd, 4);
24 |
25 | switch ($NSVheader) {
26 | case 'NSVs':
27 | if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) {
28 | $ThisFileInfo['fileformat'] = 'nsv';
29 | $ThisFileInfo['audio']['dataformat'] = 'nsv';
30 | $ThisFileInfo['video']['dataformat'] = 'nsv';
31 | $ThisFileInfo['audio']['lossless'] = false;
32 | $ThisFileInfo['video']['lossless'] = false;
33 | }
34 | break;
35 |
36 | case 'NSVf':
37 | if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) {
38 | $ThisFileInfo['fileformat'] = 'nsv';
39 | $ThisFileInfo['audio']['dataformat'] = 'nsv';
40 | $ThisFileInfo['video']['dataformat'] = 'nsv';
41 | $ThisFileInfo['audio']['lossless'] = false;
42 | $ThisFileInfo['video']['lossless'] = false;
43 | $this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']);
44 | }
45 | break;
46 |
47 | default:
48 | $ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"';
49 | return false;
50 | break;
51 | }
52 |
53 | if (!isset($ThisFileInfo['nsv']['NSVf'])) {
54 | $ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
55 | }
56 |
57 | return true;
58 | }
59 |
60 | function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) {
61 | fseek($fd, $fileoffset, SEEK_SET);
62 | $NSVsheader = fread($fd, 28);
63 | $offset = 0;
64 |
65 | $ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
66 | $offset += 4;
67 |
68 | if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') {
69 | $ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead';
70 | unset($ThisFileInfo['nsv']['NSVs']);
71 | return false;
72 | }
73 |
74 | $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
75 |
76 | $ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
77 | $offset += 4;
78 | $ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
79 | $offset += 4;
80 | $ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
81 | $offset += 2;
82 | $ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
83 | $offset += 2;
84 |
85 | $ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
86 | $offset += 1;
87 | //$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
88 | $offset += 1;
89 | //$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
90 | $offset += 1;
91 | //$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
92 | $offset += 1;
93 | //$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
94 | $offset += 1;
95 | //$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
96 | $offset += 1;
97 | //$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
98 | $offset += 1;
99 | //$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
100 | $offset += 1;
101 |
102 | switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) {
103 | case 'PCM ':
104 | $ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
105 | $offset += 1;
106 | $ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
107 | $offset += 1;
108 | $ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
109 | $offset += 2;
110 |
111 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate'];
112 | break;
113 |
114 | case 'MP3 ':
115 | case 'NONE':
116 | default:
117 | //$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
118 | $offset += 4;
119 | break;
120 | }
121 |
122 | $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x'];
123 | $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y'];
124 | $ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']);
125 | $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate'];
126 | $ThisFileInfo['video']['bits_per_sample'] = 24;
127 | $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
128 |
129 | return true;
130 | }
131 |
132 | function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) {
133 | fseek($fd, $fileoffset, SEEK_SET);
134 | $NSVfheader = fread($fd, 28);
135 | $offset = 0;
136 |
137 | $ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
138 | $offset += 4;
139 |
140 | if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') {
141 | $ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead';
142 | unset($ThisFileInfo['nsv']['NSVf']);
143 | return false;
144 | }
145 |
146 | $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
147 |
148 | $ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
149 | $offset += 4;
150 | $ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
151 | $offset += 4;
152 |
153 | if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) {
154 | $ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes';
155 | }
156 |
157 | $ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
158 | $offset += 4;
159 | $ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
160 | $offset += 4;
161 | $ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
162 | $offset += 4;
163 | $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
164 | $offset += 4;
165 |
166 | if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) {
167 | $ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
168 | return false;
169 | }
170 |
171 | $NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2']));
172 | $NSVfheaderlength = strlen($NSVfheader);
173 | $ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']);
174 | $offset += $ThisFileInfo['nsv']['NSVf']['meta_size'];
175 |
176 | if ($getTOCoffsets) {
177 | $TOCcounter = 0;
178 | while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
179 | if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
180 | $ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
181 | $offset += 4;
182 | $TOCcounter++;
183 | }
184 | }
185 | }
186 |
187 | if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') {
188 | $ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']);
189 | $CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']);
190 | foreach ($CommentPairArray as $CommentPair) {
191 | if (strstr($CommentPair, '='."\x01")) {
192 | list($key, $value) = explode('='."\x01", $CommentPair, 2);
193 | $ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
194 | }
195 | }
196 | }
197 |
198 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000;
199 | $ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds'];
200 |
201 | return true;
202 | }
203 |
204 |
205 | function NSVframerateLookup($framerateindex) {
206 | if ($framerateindex <= 127) {
207 | return (float) $framerateindex;
208 | }
209 |
210 | static $NSVframerateLookup = array();
211 | if (empty($NSVframerateLookup)) {
212 | $NSVframerateLookup[129] = (float) 29.970;
213 | $NSVframerateLookup[131] = (float) 23.976;
214 | $NSVframerateLookup[133] = (float) 14.985;
215 | $NSVframerateLookup[197] = (float) 59.940;
216 | $NSVframerateLookup[199] = (float) 47.952;
217 | }
218 | return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
219 | }
220 |
221 | }
222 |
223 |
224 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio-video.quicktime.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio-video.quicktime.php
--------------------------------------------------------------------------------
/getid3/module.audio-video.riff.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio-video.riff.php
--------------------------------------------------------------------------------
/getid3/module.audio-video.swf.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio-video.swf.php //
11 | // module for analyzing Shockwave Flash files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_swf
18 | {
19 |
20 | function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
21 | $ThisFileInfo['fileformat'] = 'swf';
22 | $ThisFileInfo['video']['dataformat'] = 'swf';
23 |
24 | // http://www.openswf.org/spec/SWFfileformat.html
25 |
26 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
27 |
28 | $SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
29 |
30 | $ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
31 | switch ($ThisFileInfo['swf']['header']['signature']) {
32 | case 'FWS':
33 | $ThisFileInfo['swf']['header']['compressed'] = false;
34 | break;
35 |
36 | case 'CWS':
37 | $ThisFileInfo['swf']['header']['compressed'] = true;
38 | break;
39 |
40 | default:
41 | $ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"';
42 | unset($ThisFileInfo['swf']);
43 | unset($ThisFileInfo['fileformat']);
44 | return false;
45 | break;
46 | }
47 | $ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
48 | $ThisFileInfo['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
49 |
50 | if ($ThisFileInfo['swf']['header']['compressed']) {
51 |
52 | if ($UncompressedFileData = @gzuncompress(substr($SWFfileData, 8))) {
53 |
54 | $SWFfileData = substr($SWFfileData, 0, 8).$UncompressedFileData;
55 |
56 | } else {
57 |
58 | $ThisFileInfo['error'][] = 'Error decompressing compressed SWF data';
59 | return false;
60 |
61 | }
62 |
63 | }
64 |
65 | $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
66 | $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
67 | $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT);
68 | for ($i = 1; $i < $FrameSizeDataLength; $i++) {
69 | $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
70 | }
71 | list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
72 | $ThisFileInfo['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
73 | $ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
74 |
75 | // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
76 | // Next in the header is the frame rate, which is kind of weird.
77 | // It is supposed to be stored as a 16bit integer, but the first byte
78 | // (or last depending on how you look at it) is completely ignored.
79 | // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
80 |
81 | // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
82 | $ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
83 | $ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
84 |
85 | $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate'];
86 | $ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20));
87 | $ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20));
88 | $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
89 |
90 | if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) {
91 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate'];
92 | }
93 |
94 |
95 | // SWF tags
96 |
97 | $CurrentOffset = 12 + $FrameSizeDataLength;
98 | $SWFdataLength = strlen($SWFfileData);
99 |
100 | while ($CurrentOffset < $SWFdataLength) {
101 |
102 | $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
103 | $TagID = ($TagIDTagLength & 0xFFFC) >> 6;
104 | $TagLength = ($TagIDTagLength & 0x003F);
105 | $CurrentOffset += 2;
106 | if ($TagLength == 0x3F) {
107 | $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
108 | $CurrentOffset += 4;
109 | }
110 |
111 | unset($TagData);
112 | $TagData['offset'] = $CurrentOffset;
113 | $TagData['size'] = $TagLength;
114 | $TagData['id'] = $TagID;
115 | $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
116 | switch ($TagID) {
117 | case 0: // end of movie
118 | break 2;
119 |
120 | case 9: // Set background color
121 | //$ThisFileInfo['swf']['tags'][] = $TagData;
122 | $ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
123 | break;
124 |
125 | default:
126 | if ($ReturnAllTagData) {
127 | $ThisFileInfo['swf']['tags'][] = $TagData;
128 | }
129 | break;
130 | }
131 |
132 | $CurrentOffset += $TagLength;
133 | }
134 |
135 | return true;
136 | }
137 |
138 | }
139 |
140 |
141 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.aac.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.aac.php
--------------------------------------------------------------------------------
/getid3/module.audio.ac3.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.ac3.php
--------------------------------------------------------------------------------
/getid3/module.audio.au.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.au.php //
11 | // module for analyzing AU files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_au
18 | {
19 |
20 | function getid3_au(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $AUheader = fread($fd, 8);
24 |
25 | if (substr($AUheader, 0, 4) != '.snd') {
26 | $ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"';
27 | return false;
28 | }
29 |
30 | // shortcut
31 | $ThisFileInfo['au'] = array();
32 | $thisfile_au = &$ThisFileInfo['au'];
33 |
34 | $ThisFileInfo['fileformat'] = 'au';
35 | $ThisFileInfo['audio']['dataformat'] = 'au';
36 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
37 | $thisfile_au['encoding'] = 'ISO-8859-1';
38 |
39 | $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
40 | $AUheader .= fread($fd, $thisfile_au['header_length'] - 8);
41 | $ThisFileInfo['avdataoffset'] += $thisfile_au['header_length'];
42 |
43 | $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
44 | $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
45 | $thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
46 | $thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
47 | $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
48 |
49 | $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
50 | $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
51 | if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
52 | $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
53 | } else {
54 | unset($thisfile_au['bits_per_sample']);
55 | }
56 |
57 | $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate'];
58 | $ThisFileInfo['audio']['channels'] = $thisfile_au['channels'];
59 |
60 | if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) {
61 | $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"';
62 | }
63 |
64 | $ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
65 | $ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds'];
66 |
67 | return true;
68 | }
69 |
70 | function AUdataFormatNameLookup($id) {
71 | static $AUdataFormatNameLookup = array(
72 | 0 => 'unspecified format',
73 | 1 => '8-bit mu-law',
74 | 2 => '8-bit linear',
75 | 3 => '16-bit linear',
76 | 4 => '24-bit linear',
77 | 5 => '32-bit linear',
78 | 6 => 'floating-point',
79 | 7 => 'double-precision float',
80 | 8 => 'fragmented sampled data',
81 | 9 => 'SUN_FORMAT_NESTED',
82 | 10 => 'DSP program',
83 | 11 => '8-bit fixed-point',
84 | 12 => '16-bit fixed-point',
85 | 13 => '24-bit fixed-point',
86 | 14 => '32-bit fixed-point',
87 |
88 | 16 => 'non-audio display data',
89 | 17 => 'SND_FORMAT_MULAW_SQUELCH',
90 | 18 => '16-bit linear with emphasis',
91 | 19 => '16-bit linear with compression',
92 | 20 => '16-bit linear with emphasis + compression',
93 | 21 => 'Music Kit DSP commands',
94 | 22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
95 | 23 => 'CCITT g.721 4-bit ADPCM',
96 | 24 => 'CCITT g.722 ADPCM',
97 | 25 => 'CCITT g.723 3-bit ADPCM',
98 | 26 => 'CCITT g.723 5-bit ADPCM',
99 | 27 => 'A-Law 8-bit'
100 | );
101 | return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
102 | }
103 |
104 | function AUdataFormatBitsPerSampleLookup($id) {
105 | static $AUdataFormatBitsPerSampleLookup = array(
106 | 1 => 8,
107 | 2 => 8,
108 | 3 => 16,
109 | 4 => 24,
110 | 5 => 32,
111 | 6 => 32,
112 | 7 => 64,
113 |
114 | 11 => 8,
115 | 12 => 16,
116 | 13 => 24,
117 | 14 => 32,
118 |
119 | 18 => 16,
120 | 19 => 16,
121 | 20 => 16,
122 |
123 | 23 => 16,
124 |
125 | 25 => 16,
126 | 26 => 16,
127 | 27 => 8
128 | );
129 | return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
130 | }
131 |
132 | function AUdataFormatUsedBitsPerSampleLookup($id) {
133 | static $AUdataFormatUsedBitsPerSampleLookup = array(
134 | 1 => 8,
135 | 2 => 8,
136 | 3 => 16,
137 | 4 => 24,
138 | 5 => 32,
139 | 6 => 32,
140 | 7 => 64,
141 |
142 | 11 => 8,
143 | 12 => 16,
144 | 13 => 24,
145 | 14 => 32,
146 |
147 | 18 => 16,
148 | 19 => 16,
149 | 20 => 16,
150 |
151 | 23 => 4,
152 |
153 | 25 => 3,
154 | 26 => 5,
155 | 27 => 8,
156 | );
157 | return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
158 | }
159 |
160 | }
161 |
162 |
163 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.avr.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.avr.php //
11 | // module for analyzing AVR Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_avr
18 | {
19 |
20 | function getid3_avr(&$fd, &$ThisFileInfo) {
21 |
22 | // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
23 | // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
24 | // offset type length name comments
25 | // ---------------------------------------------------------------------
26 | // 0 char 4 ID format ID == "2BIT"
27 | // 4 char 8 name sample name (unused space filled with 0)
28 | // 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo
29 | // With stereo, samples are alternated,
30 | // the first voice is the left :
31 | // (LRLRLRLRLRLRLRLRLR...)
32 | // 14 short 1 resolution 8, 12 or 16 (bits)
33 | // 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed
34 | // 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on
35 | // 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127
36 | // 0xFFFF means "no MIDI note defined"
37 | // 22 byte 1 Replay speed Frequence in the Replay software
38 | // 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
39 | // 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
40 | // 6=43.885 Khz, 7=47.261 Khz
41 | // -1 (0xFF)=no defined Frequence
42 | // 23 byte 3 sample rate in Hertz
43 | // 26 long 1 size in bytes (2 * bytes in stereo)
44 | // 30 long 1 loop begin 0 for no loop
45 | // 34 long 1 loop size equal to 'size' for no loop
46 | // 38 short 2 Reserved, MIDI keyboard split */
47 | // 40 short 2 Reserved, sample compression */
48 | // 42 short 2 Reserved */
49 | // 44 char 20; Additional filename space, used if (name[7] != 0)
50 | // 64 byte 64 user data
51 | // 128 bytes ? sample data (12 bits samples are coded on 16 bits:
52 | // 0000 xxxx xxxx xxxx)
53 | // ---------------------------------------------------------------------
54 |
55 | // Note that all values are in motorola (big-endian) format, and that long is
56 | // assumed to be 4 bytes, and short 2 bytes.
57 | // When reading the samples, you should handle both signed and unsigned data,
58 | // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
59 | // 8-bit data between signed/unsigned just add 127 to the sample values.
60 | // Simularly for 16-bit data you should add 32769
61 |
62 | $ThisFileInfo['fileformat'] = 'avr';
63 |
64 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
65 | $AVRheader = fread($fd, 128);
66 |
67 | $ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
68 | if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') {
69 | $ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"';
70 | unset($ThisFileInfo['fileformat']);
71 | unset($ThisFileInfo['avr']);
72 | return false;
73 | }
74 | $ThisFileInfo['avdataoffset'] += 128;
75 |
76 | $ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
77 | $ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
78 | $ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
79 | $ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
80 | $ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
81 | $ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
82 | $ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
83 | $ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
84 | $ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
85 | $ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
86 | $ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
87 | $ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
88 | $ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
89 | $ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
90 | $ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
91 | $ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
92 |
93 | $ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true);
94 | $ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true);
95 | $ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true);
96 |
97 | $ThisFileInfo['avr']['midi_notes'] = array();
98 | if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
99 | $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8;
100 | }
101 | if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
102 | $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF);
103 | }
104 |
105 | if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
106 | $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
107 | }
108 |
109 | $ThisFileInfo['audio']['dataformat'] = 'avr';
110 | $ThisFileInfo['audio']['lossless'] = true;
111 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
112 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample'];
113 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate'];
114 | $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1);
115 | $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate'];
116 | $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds'];
117 |
118 |
119 | return true;
120 | }
121 |
122 | }
123 |
124 |
125 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.bonk.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.bonk.php
--------------------------------------------------------------------------------
/getid3/module.audio.flac.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.flac.php //
11 | // module for analyzing FLAC and OggFLAC audio files //
12 | // dependencies: module.audio.ogg.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
18 |
19 | class getid3_flac
20 | {
21 |
22 | function getid3_flac(&$fd, &$ThisFileInfo) {
23 | // http://flac.sourceforge.net/format.html
24 |
25 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
26 | $StreamMarker = fread($fd, 4);
27 | if ($StreamMarker != 'fLaC') {
28 | $ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
29 | return false;
30 | }
31 | $ThisFileInfo['fileformat'] = 'flac';
32 | $ThisFileInfo['audio']['dataformat'] = 'flac';
33 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
34 | $ThisFileInfo['audio']['lossless'] = true;
35 |
36 | return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo);
37 | }
38 |
39 |
40 | function FLACparseMETAdata(&$fd, &$ThisFileInfo) {
41 |
42 | do {
43 | $METAdataBlockOffset = ftell($fd);
44 | $METAdataBlockHeader = fread($fd, 4);
45 | $METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80);
46 | $METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F;
47 | $METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
48 | $METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
49 |
50 | if ($METAdataBlockLength <= 0) {
51 | $ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
52 | break;
53 | }
54 |
55 | $ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array();
56 | $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'];
57 |
58 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset;
59 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag;
60 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType;
61 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText;
62 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength;
63 | $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = fread($fd, $METAdataBlockLength);
64 | $ThisFileInfo['avdataoffset'] = ftell($fd);
65 |
66 | switch ($METAdataBlockTypeText) {
67 |
68 | case 'STREAMINFO':
69 | if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
70 | return false;
71 | }
72 | break;
73 |
74 | case 'PADDING':
75 | // ignore
76 | break;
77 |
78 | case 'APPLICATION':
79 | if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
80 | return false;
81 | }
82 | break;
83 |
84 | case 'SEEKTABLE':
85 | if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
86 | return false;
87 | }
88 | break;
89 |
90 | case 'VORBIS_COMMENT':
91 | $OldOffset = ftell($fd);
92 | fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
93 | getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
94 | fseek($fd, $OldOffset, SEEK_SET);
95 | break;
96 |
97 | case 'CUESHEET':
98 | if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
99 | return false;
100 | }
101 | break;
102 |
103 | default:
104 | $ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
105 | break;
106 | }
107 |
108 | } while ($METAdataLastBlockFlag === false);
109 |
110 |
111 | if (isset($ThisFileInfo['flac']['STREAMINFO'])) {
112 | $ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
113 | $ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8);
114 | if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) {
115 | $ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero';
116 | return false;
117 | }
118 | $ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes'];
119 | }
120 |
121 | // set md5_data_source - built into flac 0.5+
122 | if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) {
123 |
124 | if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
125 |
126 | $ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
127 |
128 | } else {
129 |
130 | $ThisFileInfo['md5_data_source'] = '';
131 | $md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature'];
132 | for ($i = 0; $i < strlen($md5); $i++) {
133 | $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
134 | }
135 | if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
136 | unset($ThisFileInfo['md5_data_source']);
137 | }
138 |
139 | }
140 |
141 | }
142 |
143 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
144 | if ($ThisFileInfo['audio']['bits_per_sample'] == 8) {
145 | // special case
146 | // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
147 | // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
148 | $ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file';
149 | }
150 | if (!empty($ThisFileInfo['ogg']['vendor'])) {
151 | $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor'];
152 | }
153 |
154 | return true;
155 | }
156 |
157 | function FLACmetaBlockTypeLookup($blocktype) {
158 | static $FLACmetaBlockTypeLookup = array();
159 | if (empty($FLACmetaBlockTypeLookup)) {
160 | $FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
161 | $FLACmetaBlockTypeLookup[1] = 'PADDING';
162 | $FLACmetaBlockTypeLookup[2] = 'APPLICATION';
163 | $FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
164 | $FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
165 | $FLACmetaBlockTypeLookup[5] = 'CUESHEET';
166 | }
167 | return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
168 | }
169 |
170 | function FLACapplicationIDLookup($applicationid) {
171 | static $FLACapplicationIDLookup = array();
172 | if (empty($FLACapplicationIDLookup)) {
173 | // http://flac.sourceforge.net/id.html
174 | $FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol'
175 | $FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
176 | }
177 | return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
178 | }
179 |
180 | function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) {
181 | $offset = 0;
182 | $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
183 | $offset += 2;
184 | $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
185 | $offset += 2;
186 | $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
187 | $offset += 3;
188 | $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
189 | $offset += 3;
190 |
191 | $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
192 | $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20));
193 | $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1;
194 | $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1;
195 | $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
196 | $offset += 8;
197 |
198 | $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
199 | $offset += 16;
200 |
201 | if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
202 |
203 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
204 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
205 | $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels'];
206 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
207 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
208 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
209 |
210 | } else {
211 |
212 | $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
213 | return false;
214 |
215 | }
216 | return true;
217 | }
218 |
219 |
220 | function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) {
221 | $offset = 0;
222 | $ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
223 | $offset += 4;
224 | $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
225 | $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
226 | $offset = $METAdataBlockLength;
227 |
228 | return true;
229 | }
230 |
231 |
232 | function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) {
233 | $offset = 0;
234 | $METAdataBlockLength = strlen($METAdataBlockData);
235 | $placeholderpattern = str_repeat("\xFF", 8);
236 | while ($offset < $METAdataBlockLength) {
237 | $SampleNumberString = substr($METAdataBlockData, $offset, 8);
238 | $offset += 8;
239 | if ($SampleNumberString == $placeholderpattern) {
240 |
241 | // placeholder point
242 | @$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++;
243 | $offset += 10;
244 |
245 | } else {
246 |
247 | $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
248 | $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
249 | $offset += 8;
250 | $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
251 | $offset += 2;
252 |
253 | }
254 | }
255 | return true;
256 | }
257 |
258 | function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) {
259 | $offset = 0;
260 | $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0");
261 | $offset += 128;
262 | $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
263 | $offset += 8;
264 | $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
265 | $offset += 1;
266 |
267 | $offset += 258; // reserved
268 |
269 | $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
270 | $offset += 1;
271 |
272 | for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) {
273 | $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
274 | $offset += 8;
275 | $TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
276 | $offset += 1;
277 |
278 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
279 |
280 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12);
281 | $offset += 12;
282 |
283 | $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
284 | $offset += 1;
285 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
286 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
287 |
288 | $offset += 13; // reserved
289 |
290 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
291 | $offset += 1;
292 |
293 | for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
294 | $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
295 | $offset += 8;
296 | $IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
297 | $offset += 1;
298 |
299 | $offset += 3; // reserved
300 |
301 | $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
302 | }
303 | }
304 | return true;
305 | }
306 |
307 | }
308 |
309 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.la.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.la.php
--------------------------------------------------------------------------------
/getid3/module.audio.lpac.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.lpac.php //
11 | // module for analyzing LPAC Audio files //
12 | // dependencies: module.audio-video.riff.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
17 |
18 | class getid3_lpac
19 | {
20 |
21 | function getid3_lpac(&$fd, &$ThisFileInfo) {
22 |
23 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
24 | $LPACheader = fread($fd, 14);
25 | if (substr($LPACheader, 0, 4) != 'LPAC') {
26 | $ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
27 | return false;
28 | }
29 | $ThisFileInfo['avdataoffset'] += 14;
30 |
31 | $ThisFileInfo['fileformat'] = 'lpac';
32 | $ThisFileInfo['audio']['dataformat'] = 'lpac';
33 | $ThisFileInfo['audio']['lossless'] = true;
34 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
35 |
36 | $ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
37 | $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
38 | $ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
39 | $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
40 |
41 | $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
42 | $ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04);
43 | $ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02);
44 | $ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01);
45 |
46 | if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) {
47 | $ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set';
48 | }
49 |
50 | $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
51 | $ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
52 | $ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
53 | $ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
54 | $ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
55 | $ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
56 | $ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
57 | $ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
58 |
59 | if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) {
60 | $ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"';
61 | }
62 | switch ($ThisFileInfo['lpac']['file_version']) {
63 | case 6:
64 | if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) {
65 | $ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
66 | }
67 | if ($ThisFileInfo['lpac']['quantization'] != 20) {
68 | $ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q'];
69 | }
70 | break;
71 |
72 | default:
73 | //$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org';
74 | break;
75 | }
76 |
77 | $dummy = $ThisFileInfo;
78 | $riff = new getid3_riff($fd, $dummy);
79 | $ThisFileInfo['avdataoffset'] = $dummy['avdataoffset'];
80 | $ThisFileInfo['riff'] = $dummy['riff'];
81 | $ThisFileInfo['error'] = $dummy['error'];
82 | $ThisFileInfo['warning'] = $dummy['warning'];
83 | $ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments'];
84 | $ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate'];
85 |
86 | $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
87 |
88 | if ($ThisFileInfo['lpac']['flags']['24_bit']) {
89 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
90 | } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) {
91 | $ThisFileInfo['audio']['bits_per_sample'] = 16;
92 | } else {
93 | $ThisFileInfo['audio']['bits_per_sample'] = 8;
94 | }
95 |
96 | if ($ThisFileInfo['lpac']['flags']['fast_compress']) {
97 | // fast
98 | $ThisFileInfo['audio']['encoder_options'] = '-1';
99 | } else {
100 | switch ($ThisFileInfo['lpac']['max_prediction_order']) {
101 | case 20: // simple
102 | $ThisFileInfo['audio']['encoder_options'] = '-2';
103 | break;
104 | case 30: // medium
105 | $ThisFileInfo['audio']['encoder_options'] = '-3';
106 | break;
107 | case 40: // high
108 | $ThisFileInfo['audio']['encoder_options'] = '-4';
109 | break;
110 | case 60: // extrahigh
111 | $ThisFileInfo['audio']['encoder_options'] = '-5';
112 | break;
113 | }
114 | }
115 |
116 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
117 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
118 |
119 | return true;
120 | }
121 |
122 | }
123 |
124 |
125 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.mod.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.mod.php //
11 | // module for analyzing MOD Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_mod
18 | {
19 |
20 | // new combined constructor
21 | function getid3_mod(&$fd, &$ThisFileInfo, $option) {
22 |
23 | if ($option === 'mod') {
24 | $this->getMODheaderFilepointer($fd, $ThisFileInfo);
25 | }
26 | elseif ($option === 'xm') {
27 | $this->getXMheaderFilepointer($fd, $ThisFileInfo);
28 | }
29 | elseif ($option === 'it') {
30 | $this->getITheaderFilepointer($fd, $ThisFileInfo);
31 | }
32 | elseif ($option === 's3m') {
33 | $this->getS3MheaderFilepointer($fd, $ThisFileInfo);
34 | }
35 | }
36 |
37 |
38 | function getMODheaderFilepointer(&$fd, &$ThisFileInfo) {
39 |
40 | fseek($fd, $ThisFileInfo['avdataoffset'] + 1080);
41 | $FormatID = fread($fd, 4);
42 | if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) {
43 | $ThisFileInfo['error'][] = 'This is not a known type of MOD file';
44 | return false;
45 | }
46 |
47 | $ThisFileInfo['fileformat'] = 'mod';
48 |
49 | $ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()';
50 | return false;
51 | }
52 |
53 | function getXMheaderFilepointer(&$fd, &$ThisFileInfo) {
54 |
55 | fseek($fd, $ThisFileInfo['avdataoffset']);
56 | $FormatID = fread($fd, 15);
57 | if (!ereg('^Extended Module$', $FormatID)) {
58 | $ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file';
59 | return false;
60 | }
61 |
62 | $ThisFileInfo['fileformat'] = 'xm';
63 |
64 | $ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()';
65 | return false;
66 | }
67 |
68 | function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) {
69 |
70 | fseek($fd, $ThisFileInfo['avdataoffset'] + 44);
71 | $FormatID = fread($fd, 4);
72 | if (!ereg('^SCRM$', $FormatID)) {
73 | $ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file';
74 | return false;
75 | }
76 |
77 | $ThisFileInfo['fileformat'] = 's3m';
78 |
79 | $ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()';
80 | return false;
81 | }
82 |
83 | function getITheaderFilepointer(&$fd, &$ThisFileInfo) {
84 |
85 | fseek($fd, $ThisFileInfo['avdataoffset']);
86 | $FormatID = fread($fd, 4);
87 | if (!ereg('^IMPM$', $FormatID)) {
88 | $ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file';
89 | return false;
90 | }
91 |
92 | $ThisFileInfo['fileformat'] = 'it';
93 |
94 | $ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()';
95 | return false;
96 | }
97 |
98 | }
99 |
100 |
101 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.monkey.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.monkey.php
--------------------------------------------------------------------------------
/getid3/module.audio.mpc.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.mpc.php //
11 | // module for analyzing Musepack/MPEG+ Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_mpc
18 | {
19 |
20 | function getid3_mpc(&$fd, &$ThisFileInfo) {
21 | // http://www.uni-jena.de/~pfk/mpp/sv8/header.html
22 |
23 | $ThisFileInfo['mpc']['header'] = array();
24 | $thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
25 |
26 | $ThisFileInfo['fileformat'] = 'mpc';
27 | $ThisFileInfo['audio']['dataformat'] = 'mpc';
28 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
29 | $ThisFileInfo['audio']['channels'] = 2; // the format appears to be hardcoded for stereo only
30 | $ThisFileInfo['audio']['lossless'] = false;
31 |
32 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
33 |
34 | $thisfile_mpc_header['size'] = 28;
35 | $MPCheaderData = fread($fd, $thisfile_mpc_header['size']);
36 | $offset = 0;
37 |
38 | if (substr($MPCheaderData, $offset, 3) == 'MP+') {
39 |
40 | // great, this is SV7+
41 | $thisfile_mpc_header['raw']['preamble'] = substr($MPCheaderData, $offset, 3); // should be 'MP+'
42 | $offset += 3;
43 |
44 | } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', substr($MPCheaderData, 0, 4))) {
45 |
46 | // this is SV4 - SV6, handle seperately
47 | $thisfile_mpc_header['size'] = 8;
48 |
49 | // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
50 | $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
51 |
52 | // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
53 | $HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4));
54 | $HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
55 |
56 |
57 | // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
58 | // aaaa aaaa abcd dddd dddd deee eeff ffff
59 | //
60 | // a = bitrate = anything
61 | // b = IS = anything
62 | // c = MS = anything
63 | // d = streamversion = 0000000004 or 0000000005 or 0000000006
64 | // e = maxband = anything
65 | // f = blocksize = 000001 for SV5+, anything(?) for SV4
66 |
67 | $thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
68 | $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
69 | $thisfile_mpc_header['mid-side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
70 | $thisfile_mpc_header['stream_major_version'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
71 | $thisfile_mpc_header['stream_minor_version'] = 0; // no sub-version numbers before SV7
72 | $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
73 | $thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
74 |
75 | switch ($thisfile_mpc_header['stream_major_version']) {
76 | case 4:
77 | $thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
78 | break;
79 |
80 | case 5:
81 | case 6:
82 | $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
83 | break;
84 |
85 | default:
86 | $ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_major_version'].' instead';
87 | unset($ThisFileInfo['mpc']);
88 | return false;
89 | break;
90 | }
91 |
92 | if (($thisfile_mpc_header['stream_major_version'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
93 | $ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
94 | }
95 |
96 | $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
97 | $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
98 | $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels'];
99 |
100 | if ($thisfile_mpc_header['target_bitrate'] == 0) {
101 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
102 | } else {
103 | $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
104 | }
105 |
106 | $ThisFileInfo['mpc']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
107 | $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate'];
108 | $ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version'];
109 |
110 | return true;
111 |
112 | } else {
113 |
114 | $ThisFileInfo['error'][] = 'Expecting "MP+" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, $offset, 3).'"';
115 | unset($ThisFileInfo['fileformat']);
116 | unset($ThisFileInfo['mpc']);
117 | return false;
118 |
119 | }
120 |
121 | // Continue with SV7+ handling
122 | $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
123 | $offset += 1;
124 | $thisfile_mpc_header['stream_major_version'] = ($StreamVersionByte & 0x0F);
125 | $thisfile_mpc_header['stream_minor_version'] = ($StreamVersionByte & 0xF0) >> 4;
126 | $thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
127 | $offset += 4;
128 |
129 | switch ($thisfile_mpc_header['stream_major_version']) {
130 | case 7:
131 | //$ThisFileInfo['fileformat'] = 'SV7';
132 | break;
133 |
134 | default:
135 | $ThisFileInfo['error'][] = 'Only Musepack SV7 supported';
136 | return false;
137 | }
138 |
139 | $FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
140 | $offset += 4;
141 | $thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
142 | $thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
143 | $thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
144 | $thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
145 | $thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
146 | $thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
147 | $thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
148 | $thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
149 |
150 | $thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
151 | $offset += 2;
152 | $thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
153 | $offset += 2;
154 |
155 | $thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
156 | $offset += 2;
157 | $thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
158 | $offset += 2;
159 |
160 | $FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
161 | $offset += 4;
162 | $thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
163 | $thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
164 |
165 |
166 | $thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
167 | $offset += 3;
168 | $thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
169 | $offset += 1;
170 |
171 | $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
172 | $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
173 | if ($thisfile_mpc_header['sample_rate'] == 0) {
174 | $ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero';
175 | return false;
176 | }
177 | $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
178 | $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels'];
179 |
180 | $ThisFileInfo['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate'];
181 | if ($ThisFileInfo['playtime_seconds'] == 0) {
182 | $ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
183 | return false;
184 | }
185 |
186 | // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
187 | $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
188 |
189 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
190 |
191 | $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak'];
192 | $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
193 | if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
194 | $thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
195 | } else {
196 | $thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
197 | }
198 |
199 | $thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak'];
200 | $thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
201 | if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
202 | $thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
203 | } else {
204 | $thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
205 | }
206 | $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
207 |
208 | $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
209 | $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
210 |
211 | if ($thisfile_mpc_header['title_peak'] > 0) {
212 | $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
213 | } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
214 | $ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
215 | }
216 | if ($thisfile_mpc_header['album_peak'] > 0) {
217 | $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
218 | }
219 |
220 | //$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version'].'.'.$thisfile_mpc_header['stream_minor_version'].', '.$thisfile_mpc_header['encoder_version'];
221 | $ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
222 | $ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
223 |
224 | return true;
225 | }
226 |
227 | function MPCprofileNameLookup($profileid) {
228 | static $MPCprofileNameLookup = array(
229 | 0 => 'no profile',
230 | 1 => 'Experimental',
231 | 2 => 'unused',
232 | 3 => 'unused',
233 | 4 => 'unused',
234 | 5 => 'below Telephone (q = 0.0)',
235 | 6 => 'below Telephone (q = 1.0)',
236 | 7 => 'Telephone (q = 2.0)',
237 | 8 => 'Thumb (q = 3.0)',
238 | 9 => 'Radio (q = 4.0)',
239 | 10 => 'Standard (q = 5.0)',
240 | 11 => 'Extreme (q = 6.0)',
241 | 12 => 'Insane (q = 7.0)',
242 | 13 => 'BrainDead (q = 8.0)',
243 | 14 => 'above BrainDead (q = 9.0)',
244 | 15 => 'above BrainDead (q = 10.0)'
245 | );
246 | return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
247 | }
248 |
249 | function MPCfrequencyLookup($frequencyid) {
250 | static $MPCfrequencyLookup = array(
251 | 0 => 44100,
252 | 1 => 48000,
253 | 2 => 37800,
254 | 3 => 32000
255 | );
256 | return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
257 | }
258 |
259 | function MPCpeakDBLookup($intvalue) {
260 | if ($intvalue > 0) {
261 | return ((log10($intvalue) / log10(2)) - 15) * 6;
262 | }
263 | return false;
264 | }
265 |
266 | function MPCencoderVersionLookup($encoderversion) {
267 | //Encoder version * 100 (106 = 1.06)
268 | //EncoderVersion % 10 == 0 Release (1.0)
269 | //EncoderVersion % 2 == 0 Beta (1.06)
270 | //EncoderVersion % 2 == 1 Alpha (1.05a...z)
271 |
272 | if ($encoderversion == 0) {
273 | // very old version, not known exactly which
274 | return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
275 | }
276 |
277 | if (($encoderversion % 10) == 0) {
278 |
279 | // release version
280 | return number_format($encoderversion / 100, 2);
281 |
282 | } elseif (($encoderversion % 2) == 0) {
283 |
284 | // beta version
285 | return number_format($encoderversion / 100, 2).' beta';
286 |
287 | }
288 |
289 | // alpha version
290 | return number_format($encoderversion / 100, 2).' alpha';
291 | }
292 |
293 | }
294 |
295 |
296 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.optimfrog.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.optimfrog.php //
11 | // module for analyzing OptimFROG audio files //
12 | // dependencies: module.audio.riff.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
17 |
18 | class getid3_optimfrog
19 | {
20 |
21 | function getid3_optimfrog(&$fd, &$ThisFileInfo) {
22 | $ThisFileInfo['fileformat'] = 'ofr';
23 | $ThisFileInfo['audio']['dataformat'] = 'ofr';
24 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
25 | $ThisFileInfo['audio']['lossless'] = true;
26 |
27 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
28 | $OFRheader = fread($fd, 8);
29 | if (substr($OFRheader, 0, 5) == '*RIFF') {
30 |
31 | return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
32 |
33 | } elseif (substr($OFRheader, 0, 3) == 'OFR') {
34 |
35 | return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
36 |
37 | }
38 |
39 | $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
40 | unset($ThisFileInfo['fileformat']);
41 | return false;
42 | }
43 |
44 |
45 | function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
46 | // for fileformat of v4.21 and older
47 |
48 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
49 | $OptimFROGheaderData = fread($fd, 45);
50 | $ThisFileInfo['avdataoffset'] = 45;
51 |
52 | $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
53 | $OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
54 | $OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
55 | $RIFFdata = substr($OptimFROGheaderData, 1, 44);
56 | $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
57 | $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
58 |
59 | if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
60 | $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
61 | fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
62 | $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
63 | }
64 |
65 | // move the data chunk after all other chunks (if any)
66 | // so that the RIFF parser doesn't see EOF when trying
67 | // to skip over the data chunk
68 | $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
69 | getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
70 |
71 | $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
72 | $ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels'];
73 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate'];
74 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
75 | $ThisFileInfo['playtime_seconds'] = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8));
76 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
77 |
78 | return true;
79 | }
80 |
81 |
82 | function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
83 | // for fileformat of v4.50a and higher
84 |
85 | $RIFFdata = '';
86 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
87 | while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
88 | $BlockOffset = ftell($fd);
89 | $BlockData = fread($fd, 8);
90 | $offset = 8;
91 | $BlockName = substr($BlockData, 0, 4);
92 | $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
93 |
94 | if ($BlockName == 'OFRX') {
95 | $BlockName = 'OFR ';
96 | }
97 | if (!isset($ThisFileInfo['ofr'][$BlockName])) {
98 | $ThisFileInfo['ofr'][$BlockName] = array();
99 | }
100 | $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
101 |
102 | switch ($BlockName) {
103 | case 'OFR ':
104 |
105 | // shortcut
106 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
107 | $thisfile_ofr_thisblock['size'] = $BlockSize;
108 |
109 | $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
110 | switch ($BlockSize) {
111 | case 12:
112 | case 15:
113 | // good
114 | break;
115 |
116 | default:
117 | $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
118 | break;
119 | }
120 | $BlockData .= fread($fd, $BlockSize);
121 |
122 | $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
123 | $offset += 6;
124 | $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
125 | $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
126 | $offset += 1;
127 | $thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
128 | $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
129 | $offset += 1;
130 | $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
131 | $offset += 4;
132 |
133 | if ($BlockSize > 12) {
134 |
135 | // OFR 4.504b or higher
136 | $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
137 | $thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
138 | $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
139 | $offset += 2;
140 | $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
141 | $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
142 | $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
143 | $offset += 1;
144 |
145 | $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
146 | $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
147 |
148 | if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
149 | if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') {
150 | // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
151 | // between lossless and lossy other than the file extension.
152 | $ThisFileInfo['audio']['dataformat'] = 'ofs';
153 | $ThisFileInfo['audio']['lossless'] = true;
154 | }
155 | }
156 |
157 | }
158 |
159 | $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
160 | $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
161 | $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
162 | break;
163 |
164 |
165 | case 'COMP':
166 | // unlike other block types, there CAN be multiple COMP blocks
167 |
168 | $COMPdata['offset'] = $BlockOffset;
169 | $COMPdata['size'] = $BlockSize;
170 |
171 | if ($ThisFileInfo['avdataoffset'] == 0) {
172 | $ThisFileInfo['avdataoffset'] = $BlockOffset;
173 | }
174 |
175 | // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
176 | $BlockData .= fread($fd, 14);
177 | fseek($fd, $BlockSize - 14, SEEK_CUR);
178 |
179 | $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
180 | $offset += 4;
181 | $COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
182 | $offset += 4;
183 | $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
184 | $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
185 | $offset += 1;
186 | $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
187 | $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
188 | $offset += 1;
189 | $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
190 | //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
191 | $offset += 2;
192 |
193 | if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
194 |
195 | // OFR 4.504b or higher
196 | $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
197 | $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
198 | $offset += 2;
199 |
200 | }
201 |
202 | if ($COMPdata['crc_32'] == 0x454E4F4E) {
203 | // ASCII value of 'NONE' - placeholder value in v4.50a
204 | $COMPdata['crc_32'] = false;
205 | }
206 |
207 | $thisfile_ofr_thisblock[] = $COMPdata;
208 | break;
209 |
210 | case 'HEAD':
211 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
212 | $thisfile_ofr_thisblock['size'] = $BlockSize;
213 |
214 | $RIFFdata .= fread($fd, $BlockSize);
215 | break;
216 |
217 | case 'TAIL':
218 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
219 | $thisfile_ofr_thisblock['size'] = $BlockSize;
220 |
221 | if ($BlockSize > 0) {
222 | $RIFFdata .= fread($fd, $BlockSize);
223 | }
224 | break;
225 |
226 | case 'RECV':
227 | // block contains no useful meta data - simply note and skip
228 |
229 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
230 | $thisfile_ofr_thisblock['size'] = $BlockSize;
231 |
232 | fseek($fd, $BlockSize, SEEK_CUR);
233 | break;
234 |
235 |
236 | case 'APET':
237 | // APEtag v2
238 |
239 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
240 | $thisfile_ofr_thisblock['size'] = $BlockSize;
241 | $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()';
242 |
243 | fseek($fd, $BlockSize, SEEK_CUR);
244 | break;
245 |
246 |
247 | case 'MD5 ':
248 | // APEtag v2
249 |
250 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
251 | $thisfile_ofr_thisblock['size'] = $BlockSize;
252 |
253 | if ($BlockSize == 16) {
254 |
255 | $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
256 | $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
257 | $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
258 |
259 | } else {
260 |
261 | $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
262 | fseek($fd, $BlockSize, SEEK_CUR);
263 |
264 | }
265 | break;
266 |
267 |
268 | default:
269 | $thisfile_ofr_thisblock['offset'] = $BlockOffset;
270 | $thisfile_ofr_thisblock['size'] = $BlockSize;
271 |
272 | $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
273 | fseek($fd, $BlockSize, SEEK_CUR);
274 | break;
275 | }
276 | }
277 | if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
278 | $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
279 | }
280 |
281 | $ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
282 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
283 |
284 | // move the data chunk after all other chunks (if any)
285 | // so that the RIFF parser doesn't see EOF when trying
286 | // to skip over the data chunk
287 | $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
288 | getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
289 |
290 | return true;
291 | }
292 |
293 |
294 | function OptimFROGsampleTypeLookup($SampleType) {
295 | static $OptimFROGsampleTypeLookup = array(
296 | 0 => 'unsigned int (8-bit)',
297 | 1 => 'signed int (8-bit)',
298 | 2 => 'unsigned int (16-bit)',
299 | 3 => 'signed int (16-bit)',
300 | 4 => 'unsigned int (24-bit)',
301 | 5 => 'signed int (24-bit)',
302 | 6 => 'unsigned int (32-bit)',
303 | 7 => 'signed int (32-bit)',
304 | 8 => 'float 0.24 (32-bit)',
305 | 9 => 'float 16.8 (32-bit)',
306 | 10 => 'float 24.0 (32-bit)'
307 | );
308 | return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
309 | }
310 |
311 | function OptimFROGbitsPerSampleTypeLookup($SampleType) {
312 | static $OptimFROGbitsPerSampleTypeLookup = array(
313 | 0 => 8,
314 | 1 => 8,
315 | 2 => 16,
316 | 3 => 16,
317 | 4 => 24,
318 | 5 => 24,
319 | 6 => 32,
320 | 7 => 32,
321 | 8 => 32,
322 | 9 => 32,
323 | 10 => 32
324 | );
325 | return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
326 | }
327 |
328 | function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
329 | static $OptimFROGchannelConfigurationLookup = array(
330 | 0 => 'mono',
331 | 1 => 'stereo'
332 | );
333 | return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
334 | }
335 |
336 | function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
337 | static $OptimFROGchannelConfigNumChannelsLookup = array(
338 | 0 => 1,
339 | 1 => 2
340 | );
341 | return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
342 | }
343 |
344 |
345 |
346 | // function OptimFROGalgorithmNameLookup($AlgorithID) {
347 | // static $OptimFROGalgorithmNameLookup = array();
348 | // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
349 | // }
350 |
351 |
352 | function OptimFROGencoderNameLookup($EncoderID) {
353 | // version = (encoderID >> 4) + 4500
354 | // system = encoderID & 0xF
355 |
356 | $EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
357 | $EncoderSystemID = ($EncoderID & 0x0F);
358 |
359 | static $OptimFROGencoderSystemLookup = array(
360 | 0x00 => 'Windows console',
361 | 0x01 => 'Linux console',
362 | 0x0F => 'unknown'
363 | );
364 | return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
365 | }
366 |
367 | function OptimFROGcompressionLookup($CompressionID) {
368 | // mode = compression >> 3
369 | // speedup = compression & 0x07
370 |
371 | $CompressionModeID = ($CompressionID & 0xF8) >> 3;
372 | //$CompressionSpeedupID = ($CompressionID & 0x07);
373 |
374 | static $OptimFROGencoderModeLookup = array(
375 | 0x00 => 'fast',
376 | 0x01 => 'normal',
377 | 0x02 => 'high',
378 | 0x03 => 'extra', // extranew (some versions)
379 | 0x04 => 'best', // bestnew (some versions)
380 | 0x05 => 'ultra',
381 | 0x06 => 'insane',
382 | 0x07 => 'highnew',
383 | 0x08 => 'extranew',
384 | 0x09 => 'bestnew'
385 | );
386 | return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
387 | }
388 |
389 | function OptimFROGspeedupLookup($CompressionID) {
390 | // mode = compression >> 3
391 | // speedup = compression & 0x07
392 |
393 | //$CompressionModeID = ($CompressionID & 0xF8) >> 3;
394 | $CompressionSpeedupID = ($CompressionID & 0x07);
395 |
396 | static $OptimFROGencoderSpeedupLookup = array(
397 | 0x00 => '1x',
398 | 0x01 => '2x',
399 | 0x02 => '4x'
400 | );
401 |
402 | return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
403 | }
404 |
405 | }
406 |
407 |
408 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.rkau.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.shorten.php //
11 | // module for analyzing Shorten Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_rkau
18 | {
19 |
20 | function getid3_rkau(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $RKAUHeader = fread($fd, 20);
24 | if (substr($RKAUHeader, 0, 3) != 'RKA') {
25 | $ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"';
26 | return false;
27 | }
28 |
29 | $ThisFileInfo['fileformat'] = 'rkau';
30 | $ThisFileInfo['audio']['dataformat'] = 'rkau';
31 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
32 |
33 | $ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
34 | $ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
35 | if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) {
36 | $ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')';
37 | unset($ThisFileInfo['rkau']);
38 | return false;
39 | }
40 |
41 | $ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
42 | $ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
43 | $ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
44 | $ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
45 |
46 | $ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
47 | $this->RKAUqualityLookup($ThisFileInfo['rkau']);
48 |
49 | $ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
50 | $ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01));
51 | $ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02);
52 | $ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04);
53 |
54 | if ($ThisFileInfo['rkau']['flags']['streaming']) {
55 | $ThisFileInfo['avdataoffset'] += 20;
56 | $ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
57 | } else {
58 | $ThisFileInfo['avdataoffset'] += 16;
59 | $ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1;
60 | }
61 | // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
62 | // sometimes it's more, sometimes less. No idea why(?)
63 |
64 | $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless'];
65 | $ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels'];
66 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample'];
67 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate'];
68 |
69 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8));
70 | $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds'];
71 |
72 | return true;
73 |
74 | }
75 |
76 |
77 | function RKAUqualityLookup(&$RKAUdata) {
78 | $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
79 | $quality = $RKAUdata['raw']['quality'] & 0x0F;
80 |
81 | $RKAUdata['lossless'] = (($quality == 0) ? true : false);
82 | $RKAUdata['compression_level'] = $level + 1;
83 | if (!$RKAUdata['lossless']) {
84 | $RKAUdata['quality_setting'] = $quality;
85 | }
86 |
87 | return true;
88 | }
89 |
90 | }
91 |
92 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.shorten.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.shorten.php //
11 | // module for analyzing Shorten Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_shorten
18 | {
19 |
20 | function getid3_shorten(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 |
24 | $ShortenHeader = fread($fd, 8);
25 | if (substr($ShortenHeader, 0, 4) != 'ajkg') {
26 | $ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"';
27 | return false;
28 | }
29 | $ThisFileInfo['fileformat'] = 'shn';
30 | $ThisFileInfo['audio']['dataformat'] = 'shn';
31 | $ThisFileInfo['audio']['lossless'] = true;
32 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
33 |
34 | $ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
35 |
36 | fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET);
37 | $SeekTableSignatureTest = fread($fd, 12);
38 | $ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
39 | if ($ThisFileInfo['shn']['seektable']['present']) {
40 | $ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
41 | $ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length'];
42 | fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET);
43 | $SeekTableMagic = fread($fd, 4);
44 | if ($SeekTableMagic != 'SEEK') {
45 |
46 | $ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"';
47 | return false;
48 |
49 | } else {
50 |
51 | // typedef struct tag_TSeekEntry
52 | // {
53 | // unsigned long SampleNumber;
54 | // unsigned long SHNFileByteOffset;
55 | // unsigned long SHNLastBufferReadPosition;
56 | // unsigned short SHNByteGet;
57 | // unsigned short SHNBufferOffset;
58 | // unsigned short SHNFileBitOffset;
59 | // unsigned long SHNGBuffer;
60 | // unsigned short SHNBitShift;
61 | // long CBuf0[3];
62 | // long CBuf1[3];
63 | // long Offset0[4];
64 | // long Offset1[4];
65 | // }TSeekEntry;
66 |
67 | $SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16);
68 | $ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
69 | //$ThisFileInfo['shn']['seektable']['entries'] = array();
70 | //$SeekTableOffset = 0;
71 | //for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) {
72 | // $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
73 | // $SeekTableOffset += 4;
74 | // $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
75 | // $SeekTableOffset += 4;
76 | // $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
77 | // $SeekTableOffset += 4;
78 | // $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
79 | // $SeekTableOffset += 2;
80 | // $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
81 | // $SeekTableOffset += 2;
82 | // $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
83 | // $SeekTableOffset += 2;
84 | // $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
85 | // $SeekTableOffset += 4;
86 | // $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
87 | // $SeekTableOffset += 2;
88 | // for ($j = 0; $j < 3; $j++) {
89 | // $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
90 | // $SeekTableOffset += 4;
91 | // }
92 | // for ($j = 0; $j < 3; $j++) {
93 | // $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
94 | // $SeekTableOffset += 4;
95 | // }
96 | // for ($j = 0; $j < 4; $j++) {
97 | // $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
98 | // $SeekTableOffset += 4;
99 | // }
100 | // for ($j = 0; $j < 4; $j++) {
101 | // $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
102 | // $SeekTableOffset += 4;
103 | // }
104 | //
105 | // $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry;
106 | //}
107 |
108 | }
109 |
110 | }
111 |
112 | if ((bool) ini_get('safe_mode')) {
113 | $ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
114 | return false;
115 | }
116 |
117 | if (GETID3_OS_ISWINDOWS) {
118 |
119 | $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
120 | foreach ($RequiredFiles as $required_file) {
121 | if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
122 | $ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist';
123 | return false;
124 | }
125 | }
126 | $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 44';
127 |
128 | } else {
129 |
130 | static $shorten_present;
131 | if (!isset($shorten_present)) {
132 | $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
133 | }
134 | if (!$shorten_present) {
135 | $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin';
136 | return false;
137 | }
138 | $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x "'.$ThisFileInfo['filenamepath'].'" - | head -c 44';
139 |
140 | }
141 |
142 | $output = `$commandline`;
143 |
144 | if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
145 |
146 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
147 |
148 | $DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, 16));
149 | $ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
150 | $ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
151 | $ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
152 |
153 | if (substr($output, 36, 4) == 'data') {
154 |
155 | $ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 40, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
156 |
157 | } else {
158 |
159 | $ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
160 | return false;
161 |
162 | }
163 |
164 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8;
165 |
166 | } else {
167 |
168 | $ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing';
169 | return false;
170 |
171 | }
172 |
173 | return true;
174 | }
175 |
176 | }
177 |
178 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.tta.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.tta.php //
11 | // module for analyzing TTA Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_tta
18 | {
19 |
20 | function getid3_tta(&$fd, &$ThisFileInfo) {
21 |
22 | $ThisFileInfo['fileformat'] = 'tta';
23 | $ThisFileInfo['audio']['dataformat'] = 'tta';
24 | $ThisFileInfo['audio']['lossless'] = true;
25 | $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
26 |
27 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
28 | $ttaheader = fread($fd, 26);
29 |
30 | $ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3);
31 | if ($ThisFileInfo['tta']['magic'] != 'TTA') {
32 | $ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"';
33 | unset($ThisFileInfo['fileformat']);
34 | unset($ThisFileInfo['audio']);
35 | unset($ThisFileInfo['tta']);
36 | return false;
37 | }
38 |
39 | switch ($ttaheader{3}) {
40 | case "\x01": // TTA v1.x
41 | case "\x02": // TTA v1.x
42 | case "\x03": // TTA v1.x
43 | // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
44 | $ThisFileInfo['tta']['major_version'] = 1;
45 | $ThisFileInfo['avdataoffset'] += 16;
46 |
47 | $ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3});
48 | $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
49 | $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
50 | $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
51 | $ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
52 |
53 | $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
54 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate'];
55 | break;
56 |
57 | case '2': // TTA v2.x
58 | // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
59 | $ThisFileInfo['tta']['major_version'] = 2;
60 | $ThisFileInfo['avdataoffset'] += 20;
61 |
62 | $ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
63 | $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
64 | $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
65 | $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
66 | $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
67 | $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
68 |
69 | $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
70 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
71 | break;
72 |
73 | case '1': // TTA v3.x
74 | // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
75 | $ThisFileInfo['tta']['major_version'] = 3;
76 | $ThisFileInfo['avdataoffset'] += 26;
77 |
78 | $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup()
79 | $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
80 | $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
81 | $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
82 | $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
83 | $ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
84 | $ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
85 |
86 | $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
87 | break;
88 |
89 | default:
90 | $ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3};
91 | return false;
92 | break;
93 | }
94 |
95 | $ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version'];
96 | $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample'];
97 | $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate'];
98 | $ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels'];
99 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
100 |
101 | return true;
102 | }
103 |
104 | }
105 |
106 |
107 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.voc.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.audio.voc.php //
11 | // module for analyzing Creative VOC Audio files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_voc
18 | {
19 |
20 | function getid3_voc(&$fd, &$ThisFileInfo) {
21 |
22 | $OriginalAVdataOffset = $ThisFileInfo['avdataoffset'];
23 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
24 | $VOCheader = fread($fd, 26);
25 |
26 | if (substr($VOCheader, 0, 19) != 'Creative Voice File') {
27 | $ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"';
28 | return false;
29 | }
30 |
31 | // shortcuts
32 | $thisfile_audio = &$ThisFileInfo['audio'];
33 | $ThisFileInfo['voc'] = array();
34 | $thisfile_voc = &$ThisFileInfo['voc'];
35 |
36 | $ThisFileInfo['fileformat'] = 'voc';
37 | $thisfile_audio['dataformat'] = 'voc';
38 | $thisfile_audio['bitrate_mode'] = 'cbr';
39 | $thisfile_audio['lossless'] = true;
40 | $thisfile_audio['channels'] = 1; // might be overriden below
41 | $thisfile_audio['bits_per_sample'] = 8; // might be overriden below
42 |
43 | // byte # Description
44 | // ------ ------------------------------------------
45 | // 00-12 'Creative Voice File'
46 | // 13 1A (eof to abort printing of file)
47 | // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
48 | // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01)
49 | // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
50 |
51 | $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
52 | $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
53 | $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
54 |
55 | do {
56 |
57 | $BlockOffset = ftell($fd);
58 | $BlockData = fread($fd, 4);
59 | $BlockType = ord($BlockData{0});
60 | $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
61 | $ThisBlock = array();
62 |
63 | @$thisfile_voc['blocktypes'][$BlockType]++;
64 | switch ($BlockType) {
65 | case 0: // Terminator
66 | // do nothing, we'll break out of the loop down below
67 | break;
68 |
69 | case 1: // Sound data
70 | $BlockData .= fread($fd, 2);
71 | if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
72 | $ThisFileInfo['avdataoffset'] = ftell($fd);
73 | }
74 | fseek($fd, $BlockSize - 2, SEEK_CUR);
75 |
76 | $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
77 | $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
78 |
79 | $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
80 | if ($ThisBlock['compression_type'] <= 3) {
81 | $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
82 | }
83 |
84 | // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
85 | if (empty($thisfile_audio['sample_rate'])) {
86 | // SR byte = 256 - (1000000 / sample_rate)
87 | $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
88 | }
89 | break;
90 |
91 | case 2: // Sound continue
92 | case 3: // Silence
93 | case 4: // Marker
94 | case 6: // Repeat
95 | case 7: // End repeat
96 | // nothing useful, just skip
97 | fseek($fd, $BlockSize, SEEK_CUR);
98 | break;
99 |
100 | case 8: // Extended
101 | $BlockData .= fread($fd, 4);
102 |
103 | //00-01 Time Constant:
104 | // Mono: 65536 - (256000000 / sample_rate)
105 | // Stereo: 65536 - (256000000 / (sample_rate * 2))
106 | $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
107 | $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
108 | $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
109 |
110 | $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
111 | $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
112 | break;
113 |
114 | case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
115 | $BlockData .= fread($fd, 12);
116 | if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
117 | $ThisFileInfo['avdataoffset'] = ftell($fd);
118 | }
119 | fseek($fd, $BlockSize - 12, SEEK_CUR);
120 |
121 | $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
122 | $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
123 | $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
124 | $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
125 |
126 | $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
127 | if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
128 | $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
129 | }
130 |
131 | $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
132 | $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
133 | $thisfile_audio['channels'] = $ThisBlock['channels'];
134 | break;
135 |
136 | default:
137 | $ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
138 | fseek($fd, $BlockSize, SEEK_CUR);
139 | break;
140 | }
141 |
142 | if (!empty($ThisBlock)) {
143 | $ThisBlock['block_offset'] = $BlockOffset;
144 | $ThisBlock['block_size'] = $BlockSize;
145 | $ThisBlock['block_type_id'] = $BlockType;
146 | $thisfile_voc['blocks'][] = $ThisBlock;
147 | }
148 |
149 | } while (!feof($fd) && ($BlockType != 0));
150 |
151 | // Terminator block doesn't have size field, so seek back 3 spaces
152 | fseek($fd, -3, SEEK_CUR);
153 |
154 | ksort($thisfile_voc['blocktypes']);
155 |
156 | if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
157 | $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
158 | $thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
159 | }
160 |
161 | return true;
162 | }
163 |
164 | function VOCcompressionTypeLookup($index) {
165 | static $VOCcompressionTypeLookup = array(
166 | 0 => '8-bit',
167 | 1 => '4-bit',
168 | 2 => '2.6-bit',
169 | 3 => '2-bit'
170 | );
171 | return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
172 | }
173 |
174 | function VOCwFormatLookup($index) {
175 | static $VOCwFormatLookup = array(
176 | 0x0000 => '8-bit unsigned PCM',
177 | 0x0001 => 'Creative 8-bit to 4-bit ADPCM',
178 | 0x0002 => 'Creative 8-bit to 3-bit ADPCM',
179 | 0x0003 => 'Creative 8-bit to 2-bit ADPCM',
180 | 0x0004 => '16-bit signed PCM',
181 | 0x0006 => 'CCITT a-Law',
182 | 0x0007 => 'CCITT u-Law',
183 | 0x2000 => 'Creative 16-bit to 4-bit ADPCM'
184 | );
185 | return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
186 | }
187 |
188 | function VOCwFormatActualBitsPerSampleLookup($index) {
189 | static $VOCwFormatLookup = array(
190 | 0x0000 => 8,
191 | 0x0001 => 4,
192 | 0x0002 => 3,
193 | 0x0003 => 2,
194 | 0x0004 => 16,
195 | 0x0006 => 8,
196 | 0x0007 => 8,
197 | 0x2000 => 4
198 | );
199 | return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
200 | }
201 |
202 | }
203 |
204 |
205 | ?>
--------------------------------------------------------------------------------
/getid3/module.audio.vqf.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.audio.vqf.php
--------------------------------------------------------------------------------
/getid3/module.graphic.gif.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.graphic.gif.php //
11 | // module for analyzing GIF Image files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_gif
18 | {
19 |
20 | function getid3_gif(&$fd, &$ThisFileInfo) {
21 | $ThisFileInfo['fileformat'] = 'gif';
22 | $ThisFileInfo['video']['dataformat'] = 'gif';
23 | $ThisFileInfo['video']['lossless'] = true;
24 | $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
25 |
26 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
27 | $GIFheader = fread($fd, 13);
28 | $offset = 0;
29 |
30 | $ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
31 | $offset += 3;
32 |
33 | if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') {
34 | $ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"';
35 | unset($ThisFileInfo['fileformat']);
36 | unset($ThisFileInfo['gif']);
37 | return false;
38 | }
39 |
40 | $ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
41 | $offset += 3;
42 | $ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
43 | $offset += 2;
44 | $ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
45 | $offset += 2;
46 | $ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
47 | $offset += 1;
48 | $ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
49 | $offset += 1;
50 | $ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
51 | $offset += 1;
52 |
53 | $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width'];
54 | $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height'];
55 | $ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version'];
56 | $ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80);
57 | if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) {
58 | // Number of bits per primary color available to the original image, minus 1
59 | $ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
60 | } else {
61 | $ThisFileInfo['gif']['header']['bits_per_pixel'] = 0;
62 | }
63 | $ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40);
64 | if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
65 | // the number of bytes contained in the Global Color Table. To determine that
66 | // actual size of the color table, raise 2 to [the value of the field + 1]
67 | $ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1);
68 | $ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1;
69 | } else {
70 | $ThisFileInfo['gif']['header']['global_color_size'] = 0;
71 | }
72 | if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) {
73 | // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
74 | $ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
75 | }
76 |
77 | // if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
78 | // $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']);
79 | // $offset = 0;
80 | // for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) {
81 | // $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
82 | // $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
83 | // $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
84 | // $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
85 | // }
86 | // }
87 | //
88 | // // Image Descriptor
89 | // while (!feof($fd)) {
90 | // $NextBlockTest = fread($fd, 1);
91 | // switch ($NextBlockTest) {
92 | //
93 | // case ',': // ',' - Image separator character
94 | //
95 | // $ImageDescriptorData = $NextBlockTest.fread($fd, 9);
96 | // $ImageDescriptor = array();
97 | // $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
98 | // $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
99 | // $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
100 | // $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
101 | // $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1));
102 | // $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80);
103 | // $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40);
104 | // $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor;
105 | //
106 | // if ($ImageDescriptor['flags']['use_local_color_map']) {
107 | //
108 | // $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
109 | // return true;
110 | //
111 | // }
112 | //echo 'Start of raster data: '.ftell($fd).'
';
113 | // $RasterData = array();
114 | // $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
115 | // $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
116 | // $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData;
117 | //
118 | // $CurrentCodeSize = $RasterData['code_size'] + 1;
119 | // for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) {
120 | // $DefaultDataLookupTable[$i] = chr($i);
121 | // }
122 | // $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code
123 | // $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code
124 | //
125 | //
126 | // $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
127 | // echo 'Clear Code: '.$NextValue.'
';
128 | //
129 | // $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
130 | // echo 'First Color: '.$NextValue.'
';
131 | //
132 | // $Prefix = $NextValue;
133 | //$i = 0;
134 | // while ($i++ < 20) {
135 | // $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
136 | // echo $NextValue.'
';
137 | // }
138 | //return true;
139 | // break;
140 | //
141 | // case '!':
142 | // // GIF Extension Block
143 | // $ExtensionBlockData = $NextBlockTest.fread($fd, 2);
144 | // $ExtensionBlock = array();
145 | // $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
146 | // $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
147 | // $ExtensionBlock['data'] = fread($fd, $ExtensionBlock['byte_length']);
148 | // $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock;
149 | // break;
150 | //
151 | // case ';':
152 | // $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1;
153 | // // GIF Terminator
154 | // break;
155 | //
156 | // default:
157 | // break;
158 | //
159 | //
160 | // }
161 | // }
162 |
163 | return true;
164 | }
165 |
166 |
167 | function GetLSBits($fd, $bits) {
168 | static $bitbuffer = '';
169 | while (strlen($bitbuffer) < $bits) {
170 | //echo 'Read another byte: '.ftell($fd).'
';
171 | $bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
172 | }
173 |
174 | $value = bindec(substr($bitbuffer, 0 - $bits));
175 | $bitbuffer = substr($bitbuffer, 0, 0 - $bits);
176 |
177 | return $value;
178 | }
179 |
180 | }
181 |
182 |
183 | ?>
--------------------------------------------------------------------------------
/getid3/module.graphic.jpg.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.graphic.jpg.php //
11 | // module for analyzing JPEG Image files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_jpg
18 | {
19 |
20 |
21 | function getid3_jpg(&$fd, &$ThisFileInfo) {
22 | $ThisFileInfo['fileformat'] = 'jpg';
23 | $ThisFileInfo['video']['dataformat'] = 'jpg';
24 | $ThisFileInfo['video']['lossless'] = false;
25 | $ThisFileInfo['video']['bits_per_sample'] = 24;
26 | $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
27 |
28 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
29 |
30 | list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize']));
31 | if ($type == 2) {
32 |
33 | $ThisFileInfo['video']['resolution_x'] = $width;
34 | $ThisFileInfo['video']['resolution_y'] = $height;
35 |
36 | if (version_compare(phpversion(), '4.2.0', '>=')) {
37 |
38 | if (function_exists('exif_read_data')) {
39 |
40 | ob_start();
41 | $ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false);
42 | $errors = ob_get_contents();
43 | if ($errors) {
44 | $ThisFileInfo['error'][] = strip_tags($errors);
45 | unset($ThisFileInfo['jpg']['exif']);
46 | }
47 | ob_end_clean();
48 |
49 | } else {
50 |
51 | $ThisFileInfo['warning'][] = 'EXIF parsing only available when compiled with --enable-exif (or php_exif.dll enabled for Windows).';
52 |
53 | }
54 |
55 | } else {
56 |
57 | $ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion();
58 |
59 | }
60 |
61 | return true;
62 |
63 | }
64 |
65 | unset($ThisFileInfo['fileformat']);
66 | return false;
67 | }
68 |
69 | }
70 |
71 |
72 | ?>
--------------------------------------------------------------------------------
/getid3/module.graphic.pcd.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.graphic.pcd.php
--------------------------------------------------------------------------------
/getid3/module.graphic.tiff.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.archive.tiff.php //
11 | // module for analyzing TIFF files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_tiff
18 | {
19 |
20 | function getid3_tiff(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $TIFFheader = fread($fd, 4);
24 |
25 | switch (substr($TIFFheader, 0, 2)) {
26 | case 'II':
27 | $ThisFileInfo['tiff']['byte_order'] = 'Intel';
28 | break;
29 | case 'MM':
30 | $ThisFileInfo['tiff']['byte_order'] = 'Motorola';
31 | break;
32 | default:
33 | $ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset'];
34 | return false;
35 | break;
36 | }
37 |
38 | $ThisFileInfo['fileformat'] = 'tiff';
39 | $ThisFileInfo['video']['dataformat'] = 'tiff';
40 | $ThisFileInfo['video']['lossless'] = true;
41 | $ThisFileInfo['tiff']['ifd'] = array();
42 | $CurrentIFD = array();
43 |
44 | $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
45 |
46 | $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
47 |
48 | while ($nextIFDoffset > 0) {
49 |
50 | $CurrentIFD['offset'] = $nextIFDoffset;
51 |
52 | fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET);
53 | $CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
54 |
55 | for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
56 | $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
57 | $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
58 | $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
59 | $CurrentIFD['fields'][$i]['raw']['offset'] = fread($fd, 4);
60 |
61 | switch ($CurrentIFD['fields'][$i]['raw']['type']) {
62 | case 1: // BYTE An 8-bit unsigned integer.
63 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
64 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']);
65 | } else {
66 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
67 | }
68 | break;
69 |
70 | case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
71 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
72 | $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
73 | } else {
74 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
75 | }
76 | break;
77 |
78 | case 3: // SHORT A 16-bit (2-byte) unsigned integer.
79 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
80 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']);
81 | } else {
82 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
83 | }
84 | break;
85 |
86 | case 4: // LONG A 32-bit (4-byte) unsigned integer.
87 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
88 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
89 | } else {
90 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
91 | }
92 | break;
93 |
94 | case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
95 | break;
96 | }
97 | }
98 |
99 | $ThisFileInfo['tiff']['ifd'][] = $CurrentIFD;
100 | $CurrentIFD = array();
101 | $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
102 |
103 | }
104 |
105 | foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) {
106 | foreach ($IFDarray['fields'] as $key => $fieldarray) {
107 | switch ($fieldarray['raw']['tag']) {
108 | case 256: // ImageWidth
109 | case 257: // ImageLength
110 | case 258: // BitsPerSample
111 | case 259: // Compression
112 | if (!isset($fieldarray['value'])) {
113 | fseek($fd, $fieldarray['offset'], SEEK_SET);
114 | $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
115 |
116 | }
117 | break;
118 |
119 | case 270: // ImageDescription
120 | case 271: // Make
121 | case 272: // Model
122 | case 305: // Software
123 | case 306: // DateTime
124 | case 315: // Artist
125 | case 316: // HostComputer
126 | if (isset($fieldarray['value'])) {
127 | $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
128 | } else {
129 | fseek($fd, $fieldarray['offset'], SEEK_SET);
130 | $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
131 |
132 | }
133 | break;
134 | }
135 | switch ($fieldarray['raw']['tag']) {
136 | case 256: // ImageWidth
137 | $ThisFileInfo['video']['resolution_x'] = $fieldarray['value'];
138 | break;
139 |
140 | case 257: // ImageLength
141 | $ThisFileInfo['video']['resolution_y'] = $fieldarray['value'];
142 | break;
143 |
144 | case 258: // BitsPerSample
145 | if (isset($fieldarray['value'])) {
146 | $ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value'];
147 | } else {
148 | $ThisFileInfo['video']['bits_per_sample'] = 0;
149 | for ($i = 0; $i < $fieldarray['raw']['length']; $i++) {
150 | $ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']);
151 | }
152 | }
153 | break;
154 |
155 | case 259: // Compression
156 | $ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
157 | break;
158 |
159 | case 270: // ImageDescription
160 | case 271: // Make
161 | case 272: // Model
162 | case 305: // Software
163 | case 306: // DateTime
164 | case 315: // Artist
165 | case 316: // HostComputer
166 | @$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
167 | break;
168 |
169 | default:
170 | break;
171 | }
172 | }
173 | }
174 |
175 | return true;
176 | }
177 |
178 |
179 | function TIFFendian2Int($bytestring, $byteorder) {
180 | if ($byteorder == 'Intel') {
181 | return getid3_lib::LittleEndian2Int($bytestring);
182 | } elseif ($byteorder == 'Motorola') {
183 | return getid3_lib::BigEndian2Int($bytestring);
184 | }
185 | return false;
186 | }
187 |
188 | function TIFFcompressionMethod($id) {
189 | static $TIFFcompressionMethod = array();
190 | if (empty($TIFFcompressionMethod)) {
191 | $TIFFcompressionMethod = array(
192 | 1 => 'Uncompressed',
193 | 2 => 'Huffman',
194 | 3 => 'Fax - CCITT 3',
195 | 5 => 'LZW',
196 | 32773 => 'PackBits',
197 | );
198 | }
199 | return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
200 | }
201 |
202 | function TIFFcommentName($id) {
203 | static $TIFFcommentName = array();
204 | if (empty($TIFFcommentName)) {
205 | $TIFFcommentName = array(
206 | 270 => 'imagedescription',
207 | 271 => 'make',
208 | 272 => 'model',
209 | 305 => 'software',
210 | 306 => 'datetime',
211 | 315 => 'artist',
212 | 316 => 'hostcomputer',
213 | );
214 | }
215 | return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
216 | }
217 |
218 | }
219 |
220 |
221 | ?>
--------------------------------------------------------------------------------
/getid3/module.misc.exe.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.misc.exe.php //
11 | // module for analyzing EXE files //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_exe
18 | {
19 |
20 | function getid3_exe(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
23 | $EXEheader = fread($fd, 28);
24 |
25 | if (substr($EXEheader, 0, 2) != 'MZ') {
26 | $ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.';
27 | return false;
28 | }
29 |
30 | $ThisFileInfo['fileformat'] = 'exe';
31 | $ThisFileInfo['exe']['mz']['magic'] = 'MZ';
32 |
33 | $ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
34 | $ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
35 | $ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
36 | $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
37 | $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
38 | $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
39 | $ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
40 | $ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
41 | $ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
42 | $ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
43 | $ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
44 | $ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
45 |
46 | $ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size'];
47 | $ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16;
48 | $ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
49 | $ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
50 |
51 | $ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()';
52 | return false;
53 |
54 | }
55 |
56 | }
57 |
58 |
59 | ?>
--------------------------------------------------------------------------------
/getid3/module.tag.apetag.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.tag.apetag.php //
11 | // module for analyzing APE tags //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | class getid3_apetag
17 | {
18 |
19 | function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
20 | $id3v1tagsize = 128;
21 | $apetagheadersize = 32;
22 | $lyrics3tagsize = 10;
23 |
24 | if ($overrideendoffset == 0) {
25 |
26 | fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
27 | $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
28 |
29 | //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
30 | if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
31 |
32 | // APE tag found before ID3v1
33 | $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
34 |
35 | //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
36 | } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
37 |
38 | // APE tag found, no ID3v1
39 | $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
40 |
41 | }
42 |
43 | } else {
44 |
45 | fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
46 | if (fread($fd, 8) == 'APETAGEX') {
47 | $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
48 | }
49 |
50 | }
51 | if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
52 |
53 | // APE tag not found
54 | unset($ThisFileInfo['ape']);
55 | return false;
56 |
57 | }
58 |
59 | // shortcut
60 | $thisfile_ape = &$ThisFileInfo['ape'];
61 |
62 | fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
63 | $APEfooterData = fread($fd, 32);
64 | if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
65 | $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
66 | return false;
67 | }
68 |
69 | if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
70 | fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
71 | $thisfile_ape['tag_offset_start'] = ftell($fd);
72 | $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
73 | } else {
74 | $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
75 | fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
76 | $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
77 | }
78 | $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
79 |
80 | if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
81 | $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
82 | unset($ThisFileInfo['id3v1']);
83 | foreach ($ThisFileInfo['warning'] as $key => $value) {
84 | if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
85 | unset($ThisFileInfo['warning'][$key]);
86 | sort($ThisFileInfo['warning']);
87 | break;
88 | }
89 | }
90 | }
91 |
92 | $offset = 0;
93 | if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
94 | if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
95 | $offset += $apetagheadersize;
96 | } else {
97 | $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
98 | return false;
99 | }
100 | }
101 |
102 | // shortcut
103 | $ThisFileInfo['replay_gain'] = array();
104 | $thisfile_replaygain = &$ThisFileInfo['replay_gain'];
105 |
106 | for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
107 | $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
108 | $offset += 4;
109 | $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
110 | $offset += 4;
111 | if (strstr(substr($APEtagData, $offset), "\x00") === false) {
112 | $ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
113 | return false;
114 | }
115 | $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
116 | $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
117 |
118 | // shortcut
119 | $thisfile_ape['items'][$item_key] = array();
120 | $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
121 |
122 | $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
123 | $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
124 | $offset += $value_size;
125 |
126 | $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
127 | switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
128 | case 0: // UTF-8
129 | case 3: // Locator (URL, filename, etc), UTF-8 encoded
130 | $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
131 | break;
132 |
133 | default: // binary data
134 | break;
135 | }
136 |
137 | switch (strtolower($item_key)) {
138 | case 'replaygain_track_gain':
139 | $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
140 | $thisfile_replaygain['track']['originator'] = 'unspecified';
141 | break;
142 |
143 | case 'replaygain_track_peak':
144 | $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
145 | $thisfile_replaygain['track']['originator'] = 'unspecified';
146 | if ($thisfile_replaygain['track']['peak'] <= 0) {
147 | $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
148 | }
149 | break;
150 |
151 | case 'replaygain_album_gain':
152 | $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
153 | $thisfile_replaygain['album']['originator'] = 'unspecified';
154 | break;
155 |
156 | case 'replaygain_album_peak':
157 | $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
158 | $thisfile_replaygain['album']['originator'] = 'unspecified';
159 | if ($thisfile_replaygain['album']['peak'] <= 0) {
160 | $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
161 | }
162 | break;
163 |
164 | case 'mp3gain_undo':
165 | list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
166 | $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
167 | $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
168 | $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
169 | break;
170 |
171 | case 'mp3gain_minmax':
172 | list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
173 | $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
174 | $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
175 | break;
176 |
177 | case 'mp3gain_album_minmax':
178 | list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
179 | $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
180 | $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
181 | break;
182 |
183 | case 'tracknumber':
184 | foreach ($thisfile_ape_items_current['data'] as $comment) {
185 | $thisfile_ape['comments']['track'][] = $comment;
186 | }
187 | break;
188 |
189 | default:
190 | foreach ($thisfile_ape_items_current['data'] as $comment) {
191 | $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
192 | }
193 | break;
194 | }
195 |
196 | }
197 | if (empty($thisfile_replaygain)) {
198 | unset($ThisFileInfo['replay_gain']);
199 | }
200 |
201 | return true;
202 | }
203 |
204 | function parseAPEheaderFooter($APEheaderFooterData) {
205 | // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
206 |
207 | // shortcut
208 | $headerfooterinfo['raw'] = array();
209 | $headerfooterinfo_raw = &$headerfooterinfo['raw'];
210 |
211 | $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
212 | if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
213 | return false;
214 | }
215 | $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
216 | $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
217 | $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
218 | $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
219 | $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
220 |
221 | $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
222 | if ($headerfooterinfo['tag_version'] >= 2) {
223 | $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
224 | }
225 | return $headerfooterinfo;
226 | }
227 |
228 | function parseAPEtagFlags($rawflagint) {
229 | // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
230 | // All are set to zero on creation and ignored on reading."
231 | // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
232 | $flags['header'] = (bool) ($rawflagint & 0x80000000);
233 | $flags['footer'] = (bool) ($rawflagint & 0x40000000);
234 | $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
235 | $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
236 | $flags['read_only'] = (bool) ($rawflagint & 0x00000001);
237 |
238 | $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
239 |
240 | return $flags;
241 | }
242 |
243 | function APEcontentTypeFlagLookup($contenttypeid) {
244 | static $APEcontentTypeFlagLookup = array(
245 | 0 => 'utf-8',
246 | 1 => 'binary',
247 | 2 => 'external',
248 | 3 => 'reserved'
249 | );
250 | return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
251 | }
252 |
253 | function APEtagItemIsUTF8Lookup($itemkey) {
254 | static $APEtagItemIsUTF8Lookup = array(
255 | 'title',
256 | 'subtitle',
257 | 'artist',
258 | 'album',
259 | 'debut album',
260 | 'publisher',
261 | 'conductor',
262 | 'track',
263 | 'composer',
264 | 'comment',
265 | 'copyright',
266 | 'publicationright',
267 | 'file',
268 | 'year',
269 | 'record date',
270 | 'record location',
271 | 'genre',
272 | 'media',
273 | 'related',
274 | 'isrc',
275 | 'abstract',
276 | 'language',
277 | 'bibliography'
278 | );
279 | return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
280 | }
281 |
282 | }
283 |
284 | ?>
--------------------------------------------------------------------------------
/getid3/module.tag.id3v1.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // module.tag.id3v1.php //
11 | // module for analyzing ID3v1 tags //
12 | // dependencies: NONE //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_id3v1
18 | {
19 |
20 | function getid3_id3v1(&$fd, &$ThisFileInfo) {
21 |
22 | fseek($fd, -256, SEEK_END);
23 | $preid3v1 = fread($fd, 128);
24 | $id3v1tag = fread($fd, 128);
25 |
26 | if (substr($id3v1tag, 0, 3) == 'TAG') {
27 |
28 | $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
29 |
30 | $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
31 | $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
32 | $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
33 | $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
34 | $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
35 | $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
36 |
37 | // If second-last byte of comment field is null and last byte of comment field is non-null
38 | // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
39 | if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
40 | $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
41 | $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
42 | }
43 | $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
44 |
45 | $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
46 | if (!empty($ParsedID3v1['genre'])) {
47 | unset($ParsedID3v1['genreid']);
48 | }
49 | if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
50 | unset($ParsedID3v1['genre']);
51 | }
52 |
53 | foreach ($ParsedID3v1 as $key => $value) {
54 | $ParsedID3v1['comments'][$key][0] = $value;
55 | }
56 |
57 | // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
58 | $GoodFormatID3v1tag = $this->GenerateID3v1Tag(
59 | $ParsedID3v1['title'],
60 | $ParsedID3v1['artist'],
61 | $ParsedID3v1['album'],
62 | $ParsedID3v1['year'],
63 | $this->LookupGenreID(@$ParsedID3v1['genre']),
64 | $ParsedID3v1['comment'],
65 | @$ParsedID3v1['track']);
66 | $ParsedID3v1['padding_valid'] = true;
67 | if ($id3v1tag !== $GoodFormatID3v1tag) {
68 | $ParsedID3v1['padding_valid'] = false;
69 | $ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
70 | }
71 |
72 | $ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize'];
73 | $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
74 |
75 | $ThisFileInfo['id3v1'] = $ParsedID3v1;
76 | }
77 |
78 | if (substr($preid3v1, 0, 3) == 'TAG') {
79 | // The way iTunes handles tags is, well, brain-damaged.
80 | // It completely ignores v1 if ID3v2 is present.
81 | // This goes as far as adding a new v1 tag *even if there already is one*
82 |
83 | // A suspected double-ID3v1 tag has been detected, but it could be that
84 | // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
85 | if (substr($preid3v1, 96, 8) == 'APETAGEX') {
86 | // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
87 | } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
88 | // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
89 | } else {
90 | // APE and Lyrics3 footers not found - assume double ID3v1
91 | $ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
92 | $ThisFileInfo['avdataend'] -= 128;
93 | }
94 | }
95 |
96 | return true;
97 | }
98 |
99 | function cutfield($str) {
100 | return trim(substr($str, 0, strcspn($str, "\x00")));
101 | }
102 |
103 | function ArrayOfGenres($allowSCMPXextended=false) {
104 | static $GenreLookup = array(
105 | 0 => 'Blues',
106 | 1 => 'Classic Rock',
107 | 2 => 'Country',
108 | 3 => 'Dance',
109 | 4 => 'Disco',
110 | 5 => 'Funk',
111 | 6 => 'Grunge',
112 | 7 => 'Hip-Hop',
113 | 8 => 'Jazz',
114 | 9 => 'Metal',
115 | 10 => 'New Age',
116 | 11 => 'Oldies',
117 | 12 => 'Other',
118 | 13 => 'Pop',
119 | 14 => 'R&B',
120 | 15 => 'Rap',
121 | 16 => 'Reggae',
122 | 17 => 'Rock',
123 | 18 => 'Techno',
124 | 19 => 'Industrial',
125 | 20 => 'Alternative',
126 | 21 => 'Ska',
127 | 22 => 'Death Metal',
128 | 23 => 'Pranks',
129 | 24 => 'Soundtrack',
130 | 25 => 'Euro-Techno',
131 | 26 => 'Ambient',
132 | 27 => 'Trip-Hop',
133 | 28 => 'Vocal',
134 | 29 => 'Jazz+Funk',
135 | 30 => 'Fusion',
136 | 31 => 'Trance',
137 | 32 => 'Classical',
138 | 33 => 'Instrumental',
139 | 34 => 'Acid',
140 | 35 => 'House',
141 | 36 => 'Game',
142 | 37 => 'Sound Clip',
143 | 38 => 'Gospel',
144 | 39 => 'Noise',
145 | 40 => 'Alt. Rock',
146 | 41 => 'Bass',
147 | 42 => 'Soul',
148 | 43 => 'Punk',
149 | 44 => 'Space',
150 | 45 => 'Meditative',
151 | 46 => 'Instrumental Pop',
152 | 47 => 'Instrumental Rock',
153 | 48 => 'Ethnic',
154 | 49 => 'Gothic',
155 | 50 => 'Darkwave',
156 | 51 => 'Techno-Industrial',
157 | 52 => 'Electronic',
158 | 53 => 'Pop-Folk',
159 | 54 => 'Eurodance',
160 | 55 => 'Dream',
161 | 56 => 'Southern Rock',
162 | 57 => 'Comedy',
163 | 58 => 'Cult',
164 | 59 => 'Gangsta Rap',
165 | 60 => 'Top 40',
166 | 61 => 'Christian Rap',
167 | 62 => 'Pop/Funk',
168 | 63 => 'Jungle',
169 | 64 => 'Native American',
170 | 65 => 'Cabaret',
171 | 66 => 'New Wave',
172 | 67 => 'Psychedelic',
173 | 68 => 'Rave',
174 | 69 => 'Showtunes',
175 | 70 => 'Trailer',
176 | 71 => 'Lo-Fi',
177 | 72 => 'Tribal',
178 | 73 => 'Acid Punk',
179 | 74 => 'Acid Jazz',
180 | 75 => 'Polka',
181 | 76 => 'Retro',
182 | 77 => 'Musical',
183 | 78 => 'Rock & Roll',
184 | 79 => 'Hard Rock',
185 | 80 => 'Folk',
186 | 81 => 'Folk/Rock',
187 | 82 => 'National Folk',
188 | 83 => 'Swing',
189 | 84 => 'Fast-Fusion',
190 | 85 => 'Bebob',
191 | 86 => 'Latin',
192 | 87 => 'Revival',
193 | 88 => 'Celtic',
194 | 89 => 'Bluegrass',
195 | 90 => 'Avantgarde',
196 | 91 => 'Gothic Rock',
197 | 92 => 'Progressive Rock',
198 | 93 => 'Psychedelic Rock',
199 | 94 => 'Symphonic Rock',
200 | 95 => 'Slow Rock',
201 | 96 => 'Big Band',
202 | 97 => 'Chorus',
203 | 98 => 'Easy Listening',
204 | 99 => 'Acoustic',
205 | 100 => 'Humour',
206 | 101 => 'Speech',
207 | 102 => 'Chanson',
208 | 103 => 'Opera',
209 | 104 => 'Chamber Music',
210 | 105 => 'Sonata',
211 | 106 => 'Symphony',
212 | 107 => 'Booty Bass',
213 | 108 => 'Primus',
214 | 109 => 'Porn Groove',
215 | 110 => 'Satire',
216 | 111 => 'Slow Jam',
217 | 112 => 'Club',
218 | 113 => 'Tango',
219 | 114 => 'Samba',
220 | 115 => 'Folklore',
221 | 116 => 'Ballad',
222 | 117 => 'Power Ballad',
223 | 118 => 'Rhythmic Soul',
224 | 119 => 'Freestyle',
225 | 120 => 'Duet',
226 | 121 => 'Punk Rock',
227 | 122 => 'Drum Solo',
228 | 123 => 'A Cappella',
229 | 124 => 'Euro-House',
230 | 125 => 'Dance Hall',
231 | 126 => 'Goa',
232 | 127 => 'Drum & Bass',
233 | 128 => 'Club-House',
234 | 129 => 'Hardcore',
235 | 130 => 'Terror',
236 | 131 => 'Indie',
237 | 132 => 'BritPop',
238 | 133 => 'Negerpunk',
239 | 134 => 'Polsk Punk',
240 | 135 => 'Beat',
241 | 136 => 'Christian Gangsta Rap',
242 | 137 => 'Heavy Metal',
243 | 138 => 'Black Metal',
244 | 139 => 'Crossover',
245 | 140 => 'Contemporary Christian',
246 | 141 => 'Christian Rock',
247 | 142 => 'Merengue',
248 | 143 => 'Salsa',
249 | 144 => 'Trash Metal',
250 | 145 => 'Anime',
251 | 146 => 'JPop',
252 | 147 => 'Synthpop',
253 |
254 | 255 => 'Unknown',
255 |
256 | 'CR' => 'Cover',
257 | 'RX' => 'Remix'
258 | );
259 |
260 | static $GenreLookupSCMPX = array();
261 | if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
262 | $GenreLookupSCMPX = $GenreLookup;
263 | // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
264 | // Extended ID3v1 genres invented by SCMPX
265 | // Note that 255 "Japanese Anime" conflicts with standard "Unknown"
266 | $GenreLookupSCMPX[240] = 'Sacred';
267 | $GenreLookupSCMPX[241] = 'Northern Europe';
268 | $GenreLookupSCMPX[242] = 'Irish & Scottish';
269 | $GenreLookupSCMPX[243] = 'Scotland';
270 | $GenreLookupSCMPX[244] = 'Ethnic Europe';
271 | $GenreLookupSCMPX[245] = 'Enka';
272 | $GenreLookupSCMPX[246] = 'Children\'s Song';
273 | $GenreLookupSCMPX[247] = 'Japanese Sky';
274 | $GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
275 | $GenreLookupSCMPX[249] = 'Japanese Doom Rock';
276 | $GenreLookupSCMPX[250] = 'Japanese J-POP';
277 | $GenreLookupSCMPX[251] = 'Japanese Seiyu';
278 | $GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
279 | $GenreLookupSCMPX[253] = 'Japanese Moemoe';
280 | $GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
281 | //$GenreLookupSCMPX[255] = 'Japanese Anime';
282 | }
283 |
284 | return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
285 | }
286 |
287 | function LookupGenreName($genreid, $allowSCMPXextended=true) {
288 | switch ($genreid) {
289 | case 'RX':
290 | case 'CR':
291 | break;
292 | default:
293 | $genreid = intval($genreid); // to handle 3 or '3' or '03'
294 | break;
295 | }
296 | $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
297 | return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
298 | }
299 |
300 | function LookupGenreID($genre, $allowSCMPXextended=false) {
301 | $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
302 | $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
303 | foreach ($GenreLookup as $key => $value) {
304 | foreach ($GenreLookup as $key => $value) {
305 | if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
306 | return $key;
307 | }
308 | }
309 | return false;
310 | }
311 | return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
312 | }
313 |
314 | function StandardiseID3v1GenreName($OriginalGenre) {
315 | if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
316 | return getid3_id3v1::LookupGenreName($GenreID);
317 | }
318 | return $OriginalGenre;
319 | }
320 |
321 | function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
322 | $ID3v1Tag = 'TAG';
323 | $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
324 | $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
325 | $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
326 | $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
327 | if (!empty($track) && ($track > 0) && ($track <= 255)) {
328 | $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
329 | $ID3v1Tag .= "\x00";
330 | if (gettype($track) == 'string') {
331 | $track = (int) $track;
332 | }
333 | $ID3v1Tag .= chr($track);
334 | } else {
335 | $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
336 | }
337 | if (($genreid < 0) || ($genreid > 147)) {
338 | $genreid = 255; // 'unknown' genre
339 | }
340 | switch (gettype($genreid)) {
341 | case 'string':
342 | case 'integer':
343 | $ID3v1Tag .= chr(intval($genreid));
344 | break;
345 | default:
346 | $ID3v1Tag .= chr(255); // 'unknown' genre
347 | break;
348 | }
349 |
350 | return $ID3v1Tag;
351 | }
352 |
353 | }
354 |
355 |
356 | ?>
--------------------------------------------------------------------------------
/getid3/module.tag.id3v2.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/module.tag.id3v2.php
--------------------------------------------------------------------------------
/getid3/module.tag.lyrics3.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | /// //
10 | // module.tag.lyrics3.php //
11 | // module for analyzing Lyrics3 tags //
12 | // dependencies: module.tag.apetag.php (optional) //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_lyrics3
18 | {
19 |
20 | function getid3_lyrics3(&$fd, &$ThisFileInfo) {
21 | // http://www.volweb.cz/str/tags.htm
22 |
23 | fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size]
24 | $lyrics3_id3v1 = fread($fd, 128 + 9 + 6);
25 | $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
26 | $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
27 | $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
28 |
29 | if ($lyrics3end == 'LYRICSEND') {
30 | // Lyrics3v1, ID3v1, no APE
31 |
32 | $lyrics3size = 5100;
33 | $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
34 | $lyrics3version = 1;
35 |
36 | } elseif ($lyrics3end == 'LYRICS200') {
37 | // Lyrics3v2, ID3v1, no APE
38 |
39 | // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
40 | $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
41 | $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
42 | $lyrics3version = 2;
43 |
44 | } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
45 | // Lyrics3v1, no ID3v1, no APE
46 |
47 | $lyrics3size = 5100;
48 | $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
49 | $lyrics3version = 1;
50 | $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
51 |
52 | } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
53 |
54 | // Lyrics3v2, no ID3v1, no APE
55 |
56 | $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
57 | $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
58 | $lyrics3version = 2;
59 |
60 | } else {
61 |
62 | if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) {
63 |
64 | fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET);
65 | $lyrics3lsz = fread($fd, 6);
66 | $lyrics3end = fread($fd, 9);
67 |
68 | if ($lyrics3end == 'LYRICSEND') {
69 | // Lyrics3v1, APE, maybe ID3v1
70 |
71 | $lyrics3size = 5100;
72 | $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
73 | $ThisFileInfo['avdataend'] = $lyrics3offset;
74 | $lyrics3version = 1;
75 | $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
76 |
77 | } elseif ($lyrics3end == 'LYRICS200') {
78 | // Lyrics3v2, APE, maybe ID3v1
79 |
80 | $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
81 | $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
82 | $lyrics3version = 2;
83 | $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
84 |
85 | }
86 |
87 | }
88 |
89 | }
90 |
91 | if (isset($lyrics3offset)) {
92 | $ThisFileInfo['avdataend'] = $lyrics3offset;
93 | $this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size);
94 |
95 | if (!isset($ThisFileInfo['ape'])) {
96 | $GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
97 | if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) {
98 | $tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']);
99 | }
100 | }
101 |
102 | }
103 |
104 | return true;
105 | }
106 |
107 | function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) {
108 | // http://www.volweb.cz/str/tags.htm
109 |
110 | fseek($fd, $endoffset, SEEK_SET);
111 | if ($length <= 0) {
112 | return false;
113 | }
114 | $rawdata = fread($fd, $length);
115 |
116 | if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
117 | if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
118 |
119 | $ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
120 | $ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
121 | $ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend'];
122 | $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
123 | $length = strlen($rawdata);
124 |
125 | } else {
126 |
127 | $ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
128 | return false;
129 |
130 | }
131 |
132 | }
133 |
134 | $ParsedLyrics3['raw']['lyrics3version'] = $version;
135 | $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
136 | $ParsedLyrics3['tag_offset_start'] = $endoffset;
137 | $ParsedLyrics3['tag_offset_end'] = $endoffset + $length;
138 |
139 | switch ($version) {
140 |
141 | case 1:
142 | if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
143 | $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
144 | $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
145 | } else {
146 | $ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
147 | return false;
148 | }
149 | break;
150 |
151 | case 2:
152 | if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
153 | $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
154 | $rawdata = $ParsedLyrics3['raw']['unparsed'];
155 | while (strlen($rawdata) > 0) {
156 | $fieldname = substr($rawdata, 0, 3);
157 | $fieldsize = (int) substr($rawdata, 3, 5);
158 | $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
159 | $rawdata = substr($rawdata, 3 + 5 + $fieldsize);
160 | }
161 |
162 | if (isset($ParsedLyrics3['raw']['IND'])) {
163 | $i = 0;
164 | $flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
165 | foreach ($flagnames as $flagname) {
166 | if (strlen($ParsedLyrics3['raw']['IND']) > ++$i) {
167 | $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1));
168 | }
169 | }
170 | }
171 |
172 | $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
173 | foreach ($fieldnametranslation as $key => $value) {
174 | if (isset($ParsedLyrics3['raw'][$key])) {
175 | $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
176 | }
177 | }
178 |
179 | if (isset($ParsedLyrics3['raw']['IMG'])) {
180 | $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
181 | foreach ($imagestrings as $key => $imagestring) {
182 | if (strpos($imagestring, '||') !== false) {
183 | $imagearray = explode('||', $imagestring);
184 | $ParsedLyrics3['images'][$key]['filename'] = $imagearray[0];
185 | $ParsedLyrics3['images'][$key]['description'] = $imagearray[1];
186 | $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds($imagearray[2]);
187 | }
188 | }
189 | }
190 | if (isset($ParsedLyrics3['raw']['LYR'])) {
191 | $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
192 | }
193 | } else {
194 | $ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
195 | return false;
196 | }
197 | break;
198 |
199 | default:
200 | $ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
201 | return false;
202 | break;
203 | }
204 |
205 |
206 | if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) {
207 | $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
208 | unset($ThisFileInfo['id3v1']);
209 | foreach ($ThisFileInfo['warning'] as $key => $value) {
210 | if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
211 | unset($ThisFileInfo['warning'][$key]);
212 | sort($ThisFileInfo['warning']);
213 | break;
214 | }
215 | }
216 | }
217 |
218 | $ThisFileInfo['lyrics3'] = $ParsedLyrics3;
219 |
220 | return true;
221 | }
222 |
223 | function Lyrics3Timestamp2Seconds($rawtimestamp) {
224 | if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) {
225 | return (int) (($regs[1] * 60) + $regs[2]);
226 | }
227 | return false;
228 | }
229 |
230 | function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
231 | $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
232 | foreach ($lyricsarray as $key => $lyricline) {
233 | $regs = array();
234 | unset($thislinetimestamps);
235 | while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) {
236 | $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
237 | $lyricline = str_replace($regs[0], '', $lyricline);
238 | }
239 | $notimestamplyricsarray[$key] = $lyricline;
240 | if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
241 | sort($thislinetimestamps);
242 | foreach ($thislinetimestamps as $timestampkey => $timestamp) {
243 | if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
244 | // timestamps only have a 1-second resolution, it's possible that multiple lines
245 | // could have the same timestamp, if so, append
246 | $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
247 | } else {
248 | $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
249 | }
250 | }
251 | }
252 | }
253 | $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
254 | if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
255 | ksort($Lyrics3data['synchedlyrics']);
256 | }
257 | return true;
258 | }
259 |
260 | function IntString2Bool($char) {
261 | if ($char == '1') {
262 | return true;
263 | } elseif ($char == '0') {
264 | return false;
265 | }
266 | return null;
267 | }
268 | }
269 |
270 |
271 | ?>
--------------------------------------------------------------------------------
/getid3/write.apetag.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/write.apetag.php
--------------------------------------------------------------------------------
/getid3/write.id3v1.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // write.id3v1.php //
11 | // module for writing ID3v1 tags //
12 | // dependencies: module.tag.id3v1.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
17 |
18 | class getid3_write_id3v1
19 | {
20 | var $filename;
21 | var $tag_data;
22 | var $warnings = array(); // any non-critical errors will be stored here
23 | var $errors = array(); // any critical errors will be stored here
24 |
25 | function getid3_write_id3v1() {
26 | return true;
27 | }
28 |
29 | function WriteID3v1() {
30 | // File MUST be writeable - CHMOD(646) at least
31 | if (is_writeable($this->filename)) {
32 | if ($fp_source = @fopen($this->filename, 'r+b')) {
33 |
34 | fseek($fp_source, -128, SEEK_END);
35 | if (fread($fp_source, 3) == 'TAG') {
36 | fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
37 | } else {
38 | fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
39 | }
40 |
41 | $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
42 | @$this->tag_data['title'],
43 | @$this->tag_data['artist'],
44 | @$this->tag_data['album'],
45 | @$this->tag_data['year'],
46 | @$this->tag_data['genreid'],
47 | @$this->tag_data['comment'],
48 | @$this->tag_data['track']);
49 | fwrite($fp_source, $new_id3v1_tag_data, 128);
50 | fclose($fp_source);
51 | return true;
52 |
53 | } else {
54 | $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
55 | return false;
56 | }
57 | }
58 | $this->errors[] = 'File is not writeable: '.$this->filename;
59 | return false;
60 | }
61 |
62 | function FixID3v1Padding() {
63 | // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
64 | // This function rewrites the ID3v1 tag with correct padding
65 |
66 | // Initialize getID3 engine
67 | $getID3 = new getID3;
68 | $ThisFileInfo = $getID3->analyze($this->filename);
69 | if (isset($ThisFileInfo['tags']['id3v1'])) {
70 | foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
71 | $id3v1data[$key] = implode(',', $value);
72 | }
73 | $this->tag_data = $id3v1data;
74 | return $this->WriteID3v1();
75 | }
76 | return false;
77 | }
78 |
79 | function RemoveID3v1() {
80 | // File MUST be writeable - CHMOD(646) at least
81 | if (is_writeable($this->filename)) {
82 | if ($fp_source = @fopen($this->filename, 'r+b')) {
83 |
84 | fseek($fp_source, -128, SEEK_END);
85 | if (fread($fp_source, 3) == 'TAG') {
86 | ftruncate($fp_source, filesize($this->filename) - 128);
87 | } else {
88 | // no ID3v1 tag to begin with - do nothing
89 | }
90 | fclose($fp_source);
91 | return true;
92 |
93 | } else {
94 | $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
95 | }
96 | } else {
97 | $this->errors[] = $this->filename.' is not writeable';
98 | }
99 | return false;
100 | }
101 |
102 | }
103 |
104 | ?>
--------------------------------------------------------------------------------
/getid3/write.id3v2.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/write.id3v2.php
--------------------------------------------------------------------------------
/getid3/write.lyrics3.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // write.lyrics3.php //
11 | // module for writing Lyrics3 tags //
12 | // dependencies: module.tag.lyrics3.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 |
17 | class getid3_write_lyrics3
18 | {
19 | var $filename;
20 | var $tag_data;
21 | //var $lyrics3_version = 2; // 1 or 2
22 | var $warnings = array(); // any non-critical errors will be stored here
23 | var $errors = array(); // any critical errors will be stored here
24 |
25 | function getid3_write_lyrics3() {
26 | return true;
27 | }
28 |
29 | function WriteLyrics3() {
30 | $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
31 | return false;
32 | }
33 |
34 | function DeleteLyrics3() {
35 | // Initialize getID3 engine
36 | $getID3 = new getID3;
37 | $ThisFileInfo = $getID3->analyze($this->filename);
38 | if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
39 | if ($fp = @fopen($this->filename, 'a+b')) {
40 |
41 | flock($fp, LOCK_EX);
42 | $oldignoreuserabort = ignore_user_abort(true);
43 |
44 | fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET);
45 | $DataAfterLyrics3 = '';
46 | if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
47 | $DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
48 | }
49 |
50 | ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
51 |
52 | if (!empty($DataAfterLyrics3)) {
53 | fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET);
54 | fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
55 | }
56 |
57 | flock($fp, LOCK_UN);
58 | fclose($fp);
59 | ignore_user_abort($oldignoreuserabort);
60 |
61 | return true;
62 |
63 | } else {
64 |
65 | $this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
66 | return false;
67 |
68 | }
69 | }
70 | // no Lyrics3 present
71 | return true;
72 | }
73 |
74 |
75 |
76 | }
77 |
78 | ?>
--------------------------------------------------------------------------------
/getid3/write.metaflac.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/write.metaflac.php
--------------------------------------------------------------------------------
/getid3/write.real.php:
--------------------------------------------------------------------------------
1 | //
4 | // available at http://getid3.sourceforge.net //
5 | // or http://www.getid3.org //
6 | /////////////////////////////////////////////////////////////////
7 | // See readme.txt for more details //
8 | /////////////////////////////////////////////////////////////////
9 | // //
10 | // write.real.php //
11 | // module for writing RealAudio/RealVideo tags //
12 | // dependencies: module.tag.real.php //
13 | // ///
14 | /////////////////////////////////////////////////////////////////
15 |
16 | class getid3_write_real
17 | {
18 | var $filename;
19 | var $tag_data;
20 | var $warnings = array(); // any non-critical errors will be stored here
21 | var $errors = array(); // any critical errors will be stored here
22 | var $paddedlength = 512; // minimum length of CONT tag in bytes
23 |
24 | function getid3_write_real() {
25 | return true;
26 | }
27 |
28 | function WriteReal() {
29 | // File MUST be writeable - CHMOD(646) at least
30 | if (is_writeable($this->filename)) {
31 | if ($fp_source = @fopen($this->filename, 'r+b')) {
32 |
33 | // Initialize getID3 engine
34 | $getID3 = new getID3;
35 | $OldThisFileInfo = $getID3->analyze($this->filename);
36 | if (empty($OldThisFileInfo['chunks']) && !empty($OldThisFileInfo['old_ra_header'])) {
37 | $this->errors[] = 'Cannot write Real tags on old-style file format';
38 | return false;
39 | }
40 |
41 | $OldPROPinfo = false;
42 | $StartOfDATA = false;
43 | foreach ($OldThisFileInfo['chunks'] as $chunknumber => $chunkarray) {
44 | if ($chunkarray['name'] == 'PROP') {
45 | $OldPROPinfo = $chunkarray;
46 | } elseif ($chunkarray['name'] = 'DATA') {
47 | $StartOfDATA = $chunkarray['offset'];
48 | }
49 | }
50 |
51 | if (!empty($OldPROPinfo['length'])) {
52 | $this->paddedlength = max($OldPROPinfo['length'], $this->paddedlength);
53 | }
54 |
55 | $new_real_tag_data = GenerateRealTag();
56 |
57 | if (@$OldPROPinfo['length'] == $new_real_tag_data) {
58 |
59 | // new data length is same as old data length - just overwrite
60 | fseek($fp_source, $OldPROPinfo['offset'], SEEK_SET);
61 | fwrite($fp_source, $new_real_tag_data);
62 |
63 | } else {
64 |
65 | if (empty($OldPROPinfo)) {
66 | // no existing PROP chunk
67 | $BeforeOffset = $StartOfDATA;
68 | $AfterOffset = $StartOfDATA;
69 | } else {
70 | // new data is longer than old data
71 | $BeforeOffset = $OldPROPinfo['offset'];
72 | $AfterOffset = $OldPROPinfo['offset'] + $OldPROPinfo['length'];
73 | }
74 |
75 |
76 | }
77 |
78 | fclose($fp_source);
79 | return true;
80 |
81 | } else {
82 | $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
83 | return false;
84 | }
85 | }
86 | $this->errors[] = 'File is not writeable: '.$this->filename;
87 | return false;
88 | }
89 |
90 | function GenerateRealTag() {
91 | $RealCONT = "\x00\x00"; // object version
92 |
93 | $RealCONT .= BigEndian2String(strlen(@$this->tag_data['title']), 4);
94 | $RealCONT .= @$this->tag_data['title'];
95 |
96 | $RealCONT .= BigEndian2String(strlen(@$this->tag_data['artist']), 4);
97 | $RealCONT .= @$this->tag_data['artist'];
98 |
99 | $RealCONT .= BigEndian2String(strlen(@$this->tag_data['copyright']), 4);
100 | $RealCONT .= @$this->tag_data['copyright'];
101 |
102 | $RealCONT .= BigEndian2String(strlen(@$this->tag_data['comment']), 4);
103 | $RealCONT .= @$this->tag_data['comment'];
104 |
105 | if ($this->paddedlength > (strlen($RealCONT) + 8)) {
106 | $RealCONT .= str_repeat("\x00", $this->paddedlength - strlen($RealCONT) - 8);
107 | }
108 |
109 | $RealCONT = 'CONT'.BigEndian2String(strlen($RealCONT) + 8, 4).$RealCONT; // CONT chunk identifier + chunk length
110 |
111 | return $RealCONT;
112 | }
113 |
114 | function RemoveReal() {
115 | // File MUST be writeable - CHMOD(646) at least
116 | if (is_writeable($this->filename)) {
117 | if ($fp_source = @fopen($this->filename, 'r+b')) {
118 |
119 | return false;
120 | //fseek($fp_source, -128, SEEK_END);
121 | //if (fread($fp_source, 3) == 'TAG') {
122 | // ftruncate($fp_source, filesize($this->filename) - 128);
123 | //} else {
124 | // // no real tag to begin with - do nothing
125 | //}
126 | fclose($fp_source);
127 | return true;
128 |
129 | } else {
130 | $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
131 | }
132 | } else {
133 | $this->errors[] = $this->filename.' is not writeable';
134 | }
135 | return false;
136 | }
137 |
138 | }
139 |
140 | ?>
--------------------------------------------------------------------------------
/getid3/write.vorbiscomment.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/getid3/write.vorbiscomment.php
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | "Radio Liefde",
5 | "genre" => "Romance",
6 | "url" => $_SERVER["SCRIPT_URI"],
7 | "bitrate" => 96,
8 | "music_directory" => "music/",
9 | "database_file" => "music.db",
10 | "buffer_size" => 16384,
11 | "max_listen_time" => 14400,
12 | "randomize_seed" => 31337
13 | );
14 |
15 | set_time_limit(0);
16 | require_once("getid3/getid3.php");
17 | $getID3 = new getID3;
18 |
19 | //load playlist
20 | if(!file_exists($settings["database_file"])) {
21 | $filenames = array_slice(scandir($settings["music_directory"]), 2);
22 |
23 | foreach($filenames as $filename) {
24 | $id3 = $getID3->analyze($settings["music_directory"].$filename);
25 | if($id3["fileformat"] == "mp3") {
26 | $playfile = array(
27 | "filename" => $id3["filename"],
28 | "filesize" => $id3["filesize"],
29 | "playtime" => $id3["playtime_seconds"],
30 | "audiostart" => $id3["avdataoffset"],
31 | "audioend" => $id3["avdataend"],
32 | "audiolength" => $id3["avdataend"] - $id3["avdataoffset"],
33 | "artist" => $id3["tags"]["id3v2"]["artist"][0],
34 | "title" => $id3["tags"]["id3v2"]["title"][0]
35 | );
36 | if(empty($playfile["artist"]) || empty($playfile["title"]))
37 | list($playfile["artist"], $playfile["title"]) = explode(" - ", substr($playfile["filename"], 0 , -4));
38 | $playfiles[] = $playfile;
39 | }
40 | }
41 |
42 | file_put_contents($settings["database_file"], serialize($playfiles));
43 | } else {
44 | $playfiles = unserialize(file_get_contents($settings["database_file"]));
45 | }
46 |
47 | //user agents
48 | $icy_data = false;
49 | foreach(array("iTunes", "VLC", "Winamp") as $agent)
50 | if(substr($_SERVER["HTTP_USER_AGENT"], 0, strlen($agent)) == $agent)
51 | $icy_data = true;
52 |
53 | //set playlist
54 | $start_time = microtime(true);
55 | srand($settings["randomize_seed"]);
56 | shuffle($playfiles);
57 |
58 | //sum playtime
59 | foreach($playfiles as $playfile)
60 | $total_playtime += $playfile["playtime"];
61 |
62 | //calculate the current song
63 | $play_pos = $start_time % $total_playtime;
64 | foreach($playfiles as $i=>$playfile) {
65 | $play_sum += $playfile["playtime"];
66 | if($play_sum > $play_pos)
67 | break;
68 | }
69 | $track_pos = ($playfiles[$i]["playtime"] - $play_sum + $play_pos) * $playfiles[$i]["audiolength"] / $playfiles[$i]["playtime"];
70 |
71 | //output headers
72 | header("Content-type: audio/mpeg");
73 | if($icy_data) {
74 | header("icy-name: ".$settings["name"]);
75 | header("icy-genre: ".$settings["genre"]);
76 | header("icy-url: ".$settings["url"]);
77 | header("icy-metaint: ".$settings["buffer_size"]);
78 | header("icy-br: ".$settings["bitrate"]);
79 | header("Content-Length: ".$settings["max_listen_time"] * $settings["bitrate"] * 128); //suppreses chuncked transfer-encoding
80 | }
81 |
82 | //play content
83 | $o = $i;
84 | $old_buffer = substr(file_get_contents($settings["music_directory"].$playfiles[$i]["filename"]), $playfiles[$i]["audiostart"] + $track_pos, $playfiles[$i]["audiolength"] - $track_pos);
85 | while(time() - $start_time < $settings["max_listen_time"]) {
86 | $i = ++$i % count($playfiles);
87 | $buffer = $old_buffer.substr(file_get_contents($settings["music_directory"].$playfiles[$i]["filename"]), $playfiles[$i]["audiostart"], $playfiles[$i]["audiolength"]);
88 |
89 | for($j = 0; $j < floor(strlen($buffer) / $settings["buffer_size"]); $j++) {
90 | if($icy_data) {
91 | if($i == $o + 1 && ($j * $settings["buffer_size"]) <= strlen($old_buffer))
92 | $payload = "StreamTitle='{$playfiles[$o]["artist"]} - {$playfiles[$o]["title"]}';".chr(0);
93 | else
94 | $payload = "StreamTitle='{$playfiles[$i]["artist"]} - {$playfiles[$i]["title"]}';".chr(0);
95 |
96 | $metadata = chr(ceil(strlen($payload) / 16)).$payload.str_repeat(chr(0), 16 - (strlen($payload) % 16));
97 | }
98 | echo substr($buffer, $j * $settings["buffer_size"], $settings["buffer_size"]).$metadata;
99 | }
100 | $o = $i;
101 | $old_buffer = substr($buffer, $j * $settings["buffer_size"]);
102 | }
103 | ?>
--------------------------------------------------------------------------------
/music/Boston Pops-John Williams - Super Mario Brothers Theme.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/music/Boston Pops-John Williams - Super Mario Brothers Theme.mp3
--------------------------------------------------------------------------------
/music/Debussy - Clair de Lune.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/music/Debussy - Clair de Lune.mp3
--------------------------------------------------------------------------------
/music/Dido - White Flag.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gadgetguru/PHP-Streaming-Audio/8d73a015e64ef36c942edfa237b7725514ba34ac/music/Dido - White Flag.mp3
--------------------------------------------------------------------------------