1 | <?php |
---|
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.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 64'; |
---|
127 | $commandline = str_replace('/', '\\', $commandline); |
---|
128 | |
---|
129 | } else { |
---|
130 | |
---|
131 | static $shorten_present; |
---|
132 | if (!isset($shorten_present)) { |
---|
133 | $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; |
---|
134 | } |
---|
135 | if (!$shorten_present) { |
---|
136 | $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin'; |
---|
137 | return false; |
---|
138 | } |
---|
139 | $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64'; |
---|
140 | |
---|
141 | } |
---|
142 | |
---|
143 | $output = `$commandline`; |
---|
144 | |
---|
145 | if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) { |
---|
146 | |
---|
147 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); |
---|
148 | |
---|
149 | $fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4)); |
---|
150 | $DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size)); |
---|
151 | $ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels']; |
---|
152 | $ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; |
---|
153 | $ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; |
---|
154 | |
---|
155 | if (substr($output, 20 + $fmt_size, 4) == 'data') { |
---|
156 | |
---|
157 | $ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; |
---|
158 | |
---|
159 | } else { |
---|
160 | |
---|
161 | $ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; |
---|
162 | return false; |
---|
163 | |
---|
164 | } |
---|
165 | |
---|
166 | $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8; |
---|
167 | |
---|
168 | } else { |
---|
169 | |
---|
170 | $ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing'; |
---|
171 | return false; |
---|
172 | |
---|
173 | } |
---|
174 | |
---|
175 | return true; |
---|
176 | } |
---|
177 | |
---|
178 | } |
---|
179 | |
---|
180 | ?> |
---|