File indexing completed on 2025-01-26 05:25:50
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 // // 0011 // module.audio.voc.php // 0012 // module for analyzing Creative VOC Audio files // 0013 // dependencies: NONE // 0014 // /// 0015 ///////////////////////////////////////////////////////////////// 0016 0017 0018 class getid3_voc extends getid3_handler 0019 { 0020 0021 public function Analyze() { 0022 $info = &$this->getid3->info; 0023 0024 $OriginalAVdataOffset = $info['avdataoffset']; 0025 $this->fseek($info['avdataoffset']); 0026 $VOCheader = $this->fread(26); 0027 0028 $magic = 'Creative Voice File'; 0029 if (substr($VOCheader, 0, 19) != $magic) { 0030 $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($VOCheader, 0, 19)).'"'; 0031 return false; 0032 } 0033 0034 // shortcuts 0035 $thisfile_audio = &$info['audio']; 0036 $info['voc'] = array(); 0037 $thisfile_voc = &$info['voc']; 0038 0039 $info['fileformat'] = 'voc'; 0040 $thisfile_audio['dataformat'] = 'voc'; 0041 $thisfile_audio['bitrate_mode'] = 'cbr'; 0042 $thisfile_audio['lossless'] = true; 0043 $thisfile_audio['channels'] = 1; // might be overriden below 0044 $thisfile_audio['bits_per_sample'] = 8; // might be overriden below 0045 0046 // byte # Description 0047 // ------ ------------------------------------------ 0048 // 00-12 'Creative Voice File' 0049 // 13 1A (eof to abort printing of file) 0050 // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation) 0051 // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) 0052 // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) 0053 0054 $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2)); 0055 $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1)); 0056 $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1)); 0057 0058 do { 0059 0060 $BlockOffset = $this->ftell(); 0061 $BlockData = $this->fread(4); 0062 $BlockType = ord($BlockData{0}); 0063 $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); 0064 $ThisBlock = array(); 0065 0066 getid3_lib::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1); 0067 switch ($BlockType) { 0068 case 0: // Terminator 0069 // do nothing, we'll break out of the loop down below 0070 break; 0071 0072 case 1: // Sound data 0073 $BlockData .= $this->fread(2); 0074 if ($info['avdataoffset'] <= $OriginalAVdataOffset) { 0075 $info['avdataoffset'] = $this->ftell(); 0076 } 0077 $this->fseek($BlockSize - 2, SEEK_CUR); 0078 0079 $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); 0080 $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); 0081 0082 $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']); 0083 if ($ThisBlock['compression_type'] <= 3) { 0084 $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); 0085 } 0086 0087 // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) 0088 if (empty($thisfile_audio['sample_rate'])) { 0089 // SR byte = 256 - (1000000 / sample_rate) 0090 $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']); 0091 } 0092 break; 0093 0094 case 2: // Sound continue 0095 case 3: // Silence 0096 case 4: // Marker 0097 case 6: // Repeat 0098 case 7: // End repeat 0099 // nothing useful, just skip 0100 $this->fseek($BlockSize, SEEK_CUR); 0101 break; 0102 0103 case 8: // Extended 0104 $BlockData .= $this->fread(4); 0105 0106 //00-01 Time Constant: 0107 // Mono: 65536 - (256000000 / sample_rate) 0108 // Stereo: 65536 - (256000000 / (sample_rate * 2)) 0109 $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2)); 0110 $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1)); 0111 $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1)); 0112 0113 $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1); 0114 $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']); 0115 break; 0116 0117 case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit 0118 $BlockData .= $this->fread(12); 0119 if ($info['avdataoffset'] <= $OriginalAVdataOffset) { 0120 $info['avdataoffset'] = $this->ftell(); 0121 } 0122 $this->fseek($BlockSize - 12, SEEK_CUR); 0123 0124 $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); 0125 $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); 0126 $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1)); 0127 $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2)); 0128 0129 $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']); 0130 if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { 0131 $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); 0132 } 0133 0134 $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate']; 0135 $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample']; 0136 $thisfile_audio['channels'] = $ThisBlock['channels']; 0137 break; 0138 0139 default: 0140 $info['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; 0141 $this->fseek($BlockSize, SEEK_CUR); 0142 break; 0143 } 0144 0145 if (!empty($ThisBlock)) { 0146 $ThisBlock['block_offset'] = $BlockOffset; 0147 $ThisBlock['block_size'] = $BlockSize; 0148 $ThisBlock['block_type_id'] = $BlockType; 0149 $thisfile_voc['blocks'][] = $ThisBlock; 0150 } 0151 0152 } while (!feof($this->getid3->fp) && ($BlockType != 0)); 0153 0154 // Terminator block doesn't have size field, so seek back 3 spaces 0155 $this->fseek(-3, SEEK_CUR); 0156 0157 ksort($thisfile_voc['blocktypes']); 0158 0159 if (!empty($thisfile_voc['compressed_bits_per_sample'])) { 0160 $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); 0161 $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 0162 } 0163 0164 return true; 0165 } 0166 0167 public function VOCcompressionTypeLookup($index) { 0168 static $VOCcompressionTypeLookup = array( 0169 0 => '8-bit', 0170 1 => '4-bit', 0171 2 => '2.6-bit', 0172 3 => '2-bit' 0173 ); 0174 return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); 0175 } 0176 0177 public function VOCwFormatLookup($index) { 0178 static $VOCwFormatLookup = array( 0179 0x0000 => '8-bit unsigned PCM', 0180 0x0001 => 'Creative 8-bit to 4-bit ADPCM', 0181 0x0002 => 'Creative 8-bit to 3-bit ADPCM', 0182 0x0003 => 'Creative 8-bit to 2-bit ADPCM', 0183 0x0004 => '16-bit signed PCM', 0184 0x0006 => 'CCITT a-Law', 0185 0x0007 => 'CCITT u-Law', 0186 0x2000 => 'Creative 16-bit to 4-bit ADPCM' 0187 ); 0188 return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); 0189 } 0190 0191 public function VOCwFormatActualBitsPerSampleLookup($index) { 0192 static $VOCwFormatLookup = array( 0193 0x0000 => 8, 0194 0x0001 => 4, 0195 0x0002 => 3, 0196 0x0003 => 2, 0197 0x0004 => 16, 0198 0x0006 => 8, 0199 0x0007 => 8, 0200 0x2000 => 4 0201 ); 0202 return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); 0203 } 0204 0205 }