- Timestamp:
- Jul 7, 2009, 10:27:37 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/charlies_content/getid3/getid3/module.audio.la.php
r3318 r3544 1 1 <?php 2 // +----------------------------------------------------------------------+ 3 // | PHP version 5 | 4 // +----------------------------------------------------------------------+ 5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | 6 // +----------------------------------------------------------------------+ 7 // | This source file is subject to version 2 of the GPL license, | 8 // | that is bundled with this package in the file license.txt and is | 9 // | available through the world-wide-web at the following url: | 10 // | http://www.gnu.org/copyleft/gpl.html | 11 // +----------------------------------------------------------------------+ 12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | 13 // +----------------------------------------------------------------------+ 14 // | Authors: James Heinrich <infoØgetid3*org> | 15 // | Allan Hansen <ahØartemis*dk> | 16 // +----------------------------------------------------------------------+ 17 // | module.audio.la.php | 18 // | Module for analyzing LA udio files | 19 // | dependencies: module.audio-video.riff.php | 20 // +----------------------------------------------------------------------+ 21 // 22 // $Id$ 23 24 25 26 class getid3_la extends getid3_handler 2 ///////////////////////////////////////////////////////////////// 3 /// getID3() by James Heinrich <info@getid3.org> // 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.la.php // 11 // module for analyzing LA 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_la 27 19 { 28 20 29 public function Analyze() { 30 31 $getid3 = $this->getid3; 32 33 $getid3->include_module('audio-video.riff'); 34 35 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); 36 $raw_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE); 37 38 $getid3->info['fileformat'] = 'la'; 39 $getid3->info['audio']['dataformat'] = 'la'; 40 $getid3->info['audio']['lossless'] = true; 41 42 $getid3->info['la']['version_major'] = (int)$raw_data{2}; 43 $getid3->info['la']['version_minor'] = (int)$raw_data{3}; 44 $getid3->info['la']['version'] = (float)$getid3->info['la']['version_major'] + ($getid3->info['la']['version_minor'] / 10); 45 46 $getid3->info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($raw_data, 4, 4)); 47 48 $wave_chunk = substr($raw_data, 8, 4); 49 if ($wave_chunk !== 'WAVE') { 50 throw new getid3_exception('Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset 8, found "'.$wave_chunk.'" ('.getid3_lib::PrintHexBytes($wave_chunk).') instead.'); 51 } 52 53 $offset = 12; 54 55 $getid3->info['la']['fmt_size'] = 24; 56 if ($getid3->info['la']['version'] >= 0.3) { 57 58 $getid3->info['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); 59 $getid3->info['la']['header_size'] = 49 + $getid3->info['la']['fmt_size'] - 24; 60 $offset += 4; 61 62 } else { 63 64 // version 0.2 didn't support additional data blocks 65 $getid3->info['la']['header_size'] = 41; 66 } 67 68 $fmt_chunk = substr($raw_data, $offset, 4); 69 if ($fmt_chunk !== 'fmt ') { 70 throw new getid3_exception('Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'); 71 } 72 $offset += 4; 73 74 $fmt_size = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); 75 $offset += 4; 76 77 $getid3->info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 2)); 78 $offset += 2; 79 80 getid3_lib::ReadSequence('LittleEndian2Int', $getid3->info['la'], $raw_data, $offset, 81 array ( 82 'channels' => 2, 83 'sample_rate' => 4, 84 'bytes_per_second' => 4, 85 'bytes_per_sample' => 2, 86 'bits_per_sample' => 2, 87 'samples' => 4 88 ) 89 ); 90 $offset += 18; 91 92 $getid3->info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int($raw_data{$offset++}); 93 94 $getid3->info['la']['flags']['seekable'] = (bool)($getid3->info['la']['raw']['flags'] & 0x01); 95 if ($getid3->info['la']['version'] >= 0.4) { 96 $getid3->info['la']['flags']['high_compression'] = (bool)($getid3->info['la']['raw']['flags'] & 0x02); 97 } 98 99 $getid3->info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); 100 $offset += 4; 101 102 // mikeØbevin*de 103 // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 104 // in earlier versions. A seekpoint is added every blocksize * seekevery 105 // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should 106 // give the number of bytes used for the seekpoints. Of course, if seeking 107 // is disabled, there are no seekpoints stored. 108 109 if ($getid3->info['la']['version'] >= 0.4) { 110 $getid3->info['la']['blocksize'] = 61440; 111 $getid3->info['la']['seekevery'] = 19; 112 } else { 113 $getid3->info['la']['blocksize'] = 73728; 114 $getid3->info['la']['seekevery'] = 16; 115 } 116 117 $getid3->info['la']['seekpoint_count'] = 0; 118 if ($getid3->info['la']['flags']['seekable']) { 119 $getid3->info['la']['seekpoint_count'] = floor($getid3->info['la']['samples'] / ($getid3->info['la']['blocksize'] * $getid3->info['la']['seekevery'])); 120 121 for ($i = 0; $i < $getid3->info['la']['seekpoint_count']; $i++) { 122 $getid3->info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); 123 $offset += 4; 124 } 125 } 126 127 if ($getid3->info['la']['version'] >= 0.3) { 128 129 // Following the main header information, the program outputs all of the 130 // seekpoints. Following these is what I called the 'footer start', 131 // i.e. the position immediately after the La audio data is finished. 132 133 $getid3->info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); 134 $offset += 4; 135 136 if ($getid3->info['la']['footerstart'] > $getid3->info['filesize']) { 137 $getid3->warning('FooterStart value points to offset '.$getid3->info['la']['footerstart'].' which is beyond end-of-file ('.$getid3->info['filesize'].')'); 138 $getid3->info['la']['footerstart'] = $getid3->info['filesize']; 139 } 140 141 } else { 142 143 // La v0.2 didn't have FooterStart value 144 $getid3->info['la']['footerstart'] = $getid3->info['avdataend']; 145 146 } 147 148 if ($getid3->info['la']['footerstart'] < $getid3->info['avdataend']) { 149 150 // Create riff header 151 $riff_data = 'WAVE'; 152 if ($getid3->info['la']['version'] == 0.2) { 153 $riff_data .= substr($raw_data, 12, 24); 154 } else { 155 $riff_data .= substr($raw_data, 16, 24); 156 } 157 if ($getid3->info['la']['footerstart'] < $getid3->info['avdataend']) { 158 fseek($getid3->fp, $getid3->info['la']['footerstart'], SEEK_SET); 159 $riff_data .= fread($getid3->fp, $getid3->info['avdataend'] - $getid3->info['la']['footerstart']); 160 } 161 $riff_data = 'RIFF'.getid3_lib::LittleEndian2String(strlen($riff_data), 4, false).$riff_data; 162 163 // Clone getid3 - messing with offsets - better safe than sorry 164 $clone = clone $getid3; 165 166 // Analyze clone by string 167 $riff = new getid3_riff($clone); 168 $riff->AnalyzeString($riff_data); 169 170 // Import from clone and destroy 171 $getid3->info['riff'] = $clone->info['riff']; 172 $getid3->warnings($clone->warnings()); 173 unset($clone); 174 } 175 176 // $getid3->info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway 177 $getid3->info['avdataend'] = $getid3->info['avdataoffset'] + $getid3->info['la']['footerstart']; 178 $getid3->info['avdataoffset'] = $getid3->info['avdataoffset'] + $offset; 179 180 $getid3->info['la']['compression_ratio'] = (float)(($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / $getid3->info['la']['uncompressed_size']); 181 $getid3->info['playtime_seconds'] = (float)($getid3->info['la']['samples'] / $getid3->info['la']['sample_rate']) / $getid3->info['la']['channels']; 182 183 $getid3->info['audio']['bitrate'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['playtime_seconds']; 184 $getid3->info['audio']['bits_per_sample'] = $getid3->info['la']['bits_per_sample']; 185 186 $getid3->info['audio']['channels'] = $getid3->info['la']['channels']; 187 $getid3->info['audio']['sample_rate'] = (int)$getid3->info['la']['sample_rate']; 188 $getid3->info['audio']['encoder'] = 'LA v'.$getid3->info['la']['version']; 189 190 return true; 191 } 21 function getid3_la(&$fd, &$ThisFileInfo) { 22 $offset = 0; 23 fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); 24 $rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); 25 26 switch (substr($rawdata, $offset, 4)) { 27 case 'LA02': 28 case 'LA03': 29 case 'LA04': 30 $ThisFileInfo['fileformat'] = 'la'; 31 $ThisFileInfo['audio']['dataformat'] = 'la'; 32 $ThisFileInfo['audio']['lossless'] = true; 33 34 $ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); 35 $ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); 36 $ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10); 37 $offset += 4; 38 39 $ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 40 $offset += 4; 41 if ($ThisFileInfo['la']['uncompressed_size'] == 0) { 42 $ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero'; 43 return false; 44 } 45 46 $WAVEchunk = substr($rawdata, $offset, 4); 47 if ($WAVEchunk !== 'WAVE') { 48 $ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.'; 49 return false; 50 } 51 $offset += 4; 52 53 $ThisFileInfo['la']['fmt_size'] = 24; 54 if ($ThisFileInfo['la']['version'] >= 0.3) { 55 56 $ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 57 $ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24; 58 $offset += 4; 59 60 } else { 61 62 // version 0.2 didn't support additional data blocks 63 $ThisFileInfo['la']['header_size'] = 41; 64 65 } 66 67 $fmt_chunk = substr($rawdata, $offset, 4); 68 if ($fmt_chunk !== 'fmt ') { 69 $ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'; 70 return false; 71 } 72 $offset += 4; 73 $fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 74 $offset += 4; 75 76 $ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); 77 $offset += 2; 78 79 $ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); 80 $offset += 2; 81 if ($ThisFileInfo['la']['channels'] == 0) { 82 $ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero'; 83 return false; 84 } 85 86 $ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 87 $offset += 4; 88 if ($ThisFileInfo['la']['sample_rate'] == 0) { 89 $ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero'; 90 return false; 91 } 92 93 $ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 94 $offset += 4; 95 $ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); 96 $offset += 2; 97 $ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); 98 $offset += 2; 99 100 $ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 101 $offset += 4; 102 103 $ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1)); 104 $offset += 1; 105 $ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01); 106 if ($ThisFileInfo['la']['version'] >= 0.4) { 107 $ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02); 108 } 109 110 $ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 111 $offset += 4; 112 113 // mikeØbevin*de 114 // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 115 // in earlier versions. A seekpoint is added every blocksize * seekevery 116 // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should 117 // give the number of bytes used for the seekpoints. Of course, if seeking 118 // is disabled, there are no seekpoints stored. 119 if ($ThisFileInfo['la']['version'] >= 0.4) { 120 $ThisFileInfo['la']['blocksize'] = 61440; 121 $ThisFileInfo['la']['seekevery'] = 19; 122 } else { 123 $ThisFileInfo['la']['blocksize'] = 73728; 124 $ThisFileInfo['la']['seekevery'] = 16; 125 } 126 127 $ThisFileInfo['la']['seekpoint_count'] = 0; 128 if ($ThisFileInfo['la']['flags']['seekable']) { 129 $ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery'])); 130 131 for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) { 132 $ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 133 $offset += 4; 134 } 135 } 136 137 if ($ThisFileInfo['la']['version'] >= 0.3) { 138 139 // Following the main header information, the program outputs all of the 140 // seekpoints. Following these is what I called the 'footer start', 141 // i.e. the position immediately after the La audio data is finished. 142 $ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); 143 $offset += 4; 144 145 if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) { 146 $ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')'; 147 $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize']; 148 } 149 150 } else { 151 152 // La v0.2 didn't have FooterStart value 153 $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend']; 154 155 } 156 157 if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { 158 if ($RIFFtempfilename = tempnam('*', 'id3')) { 159 if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) { 160 $RIFFdata = 'WAVE'; 161 if ($ThisFileInfo['la']['version'] == 0.2) { 162 $RIFFdata .= substr($rawdata, 12, 24); 163 } else { 164 $RIFFdata .= substr($rawdata, 16, 24); 165 } 166 if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { 167 fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET); 168 $RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']); 169 } 170 $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata; 171 fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); 172 $dummy = $ThisFileInfo; 173 $dummy['filesize'] = strlen($RIFFdata); 174 $dummy['avdataoffset'] = 0; 175 $dummy['avdataend'] = $dummy['filesize']; 176 177 $riff = new getid3_riff($RIFF_fp, $dummy); 178 if (empty($dummy['error'])) { 179 $ThisFileInfo['riff'] = $dummy['riff']; 180 } else { 181 $ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']); 182 } 183 unset($riff); 184 unset($dummy); 185 fclose($RIFF_fp); 186 } 187 unlink($RIFFtempfilename); 188 } 189 } 190 191 // $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway 192 $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart']; 193 $ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset; 194 195 //$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']); 196 $ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']); 197 $ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels']; 198 if ($ThisFileInfo['playtime_seconds'] == 0) { 199 $ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero'; 200 return false; 201 } 202 203 $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; 204 //$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec']; 205 $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample']; 206 break; 207 208 default: 209 if (substr($rawdata, $offset, 2) == 'LA') { 210 $ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.'; 211 } else { 212 $ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file'; 213 } 214 return false; 215 break; 216 } 217 218 $ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels']; 219 $ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate']; 220 $ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version']; 221 222 return true; 223 } 192 224 193 225 }
Note: See TracChangeset
for help on using the changeset viewer.