├── 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 --------------------------------------------------------------------------------