22 {
23 $info = &$this->getid3->info;
24
25 $offset = 0;
27 $rawdata = $this->
fread($this->getid3->fread_buffer_size());
28
29 switch (substr($rawdata, $offset, 4)) {
30 case 'LA02':
31 case 'LA03':
32 case 'LA04':
33 $info[
'fileformat'] =
'la';
34 $info[
'audio'][
'dataformat'] =
'la';
35 $info[
'audio'][
'lossless'] =
true;
36
37 $info[
'la'][
'version_major'] = (int) substr($rawdata, $offset + 2, 1);
38 $info[
'la'][
'version_minor'] = (int) substr($rawdata, $offset + 3, 1);
39 $info[
'la'][
'version'] = (float)
$info[
'la'][
'version_major'] + (
$info[
'la'][
'version_minor'] / 10);
40 $offset += 4;
41
43 $offset += 4;
44 if (
$info[
'la'][
'uncompressed_size'] == 0) {
45 $this->
error(
'Corrupt LA file: uncompressed_size == zero');
46 return false;
47 }
48
49 $WAVEchunk = substr($rawdata, $offset, 4);
50 if ($WAVEchunk !== 'WAVE') {
52 return false;
53 }
54 $offset += 4;
55
56 $info[
'la'][
'fmt_size'] = 24;
57 if (
$info[
'la'][
'version'] >= 0.3) {
58
60 $info[
'la'][
'header_size'] = 49 +
$info[
'la'][
'fmt_size'] - 24;
61 $offset += 4;
62
63 } else {
64
65
66 $info[
'la'][
'header_size'] = 41;
67
68 }
69
70 $fmt_chunk = substr($rawdata, $offset, 4);
71 if ($fmt_chunk !== 'fmt ') {
73 return false;
74 }
75 $offset += 4;
77 $offset += 4;
78
80 $offset += 2;
81
83 $offset += 2;
84 if (
$info[
'la'][
'channels'] == 0) {
85 $this->
error(
'Corrupt LA file: channels == zero');
86 return false;
87 }
88
90 $offset += 4;
91 if (
$info[
'la'][
'sample_rate'] == 0) {
92 $this->
error(
'Corrupt LA file: sample_rate == zero');
93 return false;
94 }
95
97 $offset += 4;
99 $offset += 2;
101 $offset += 2;
102
104 $offset += 4;
105
107 $offset += 1;
108 $info[
'la'][
'flags'][
'seekable'] = (bool) (
$info[
'la'][
'raw'][
'flags'] & 0x01);
109 if (
$info[
'la'][
'version'] >= 0.4) {
110 $info[
'la'][
'flags'][
'high_compression'] = (bool) (
$info[
'la'][
'raw'][
'flags'] & 0x02);
111 }
112
114 $offset += 4;
115
116
117
118
119
120
121
122 if (
$info[
'la'][
'version'] >= 0.4) {
123 $info[
'la'][
'blocksize'] = 61440;
124 $info[
'la'][
'seekevery'] = 19;
125 } else {
126 $info[
'la'][
'blocksize'] = 73728;
127 $info[
'la'][
'seekevery'] = 16;
128 }
129
130 $info[
'la'][
'seekpoint_count'] = 0;
131 if (
$info[
'la'][
'flags'][
'seekable']) {
132 $info[
'la'][
'seekpoint_count'] = floor(
$info[
'la'][
'samples'] / (
$info[
'la'][
'blocksize'] *
$info[
'la'][
'seekevery']));
133
134 for (
$i = 0;
$i <
$info[
'la'][
'seekpoint_count'];
$i++) {
136 $offset += 4;
137 }
138 }
139
140 if (
$info[
'la'][
'version'] >= 0.3) {
141
142
143
144
146 $offset += 4;
147
148 if (
$info[
'la'][
'footerstart'] >
$info[
'filesize']) {
149 $this->
warning(
'FooterStart value points to offset '.$info[
'la'][
'footerstart'].
' which is beyond end-of-file ('.
$info[
'filesize'].
')');
150 $info[
'la'][
'footerstart'] =
$info[
'filesize'];
151 }
152
153 } else {
154
155
156 $info[
'la'][
'footerstart'] =
$info[
'avdataend'];
157
158 }
159
160 if (
$info[
'la'][
'footerstart'] <
$info[
'avdataend']) {
161 if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
162 if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
163 $RIFFdata = 'WAVE';
164 if (
$info[
'la'][
'version'] == 0.2) {
165 $RIFFdata .= substr($rawdata, 12, 24);
166 } else {
167 $RIFFdata .= substr($rawdata, 16, 24);
168 }
169 if (
$info[
'la'][
'footerstart'] <
$info[
'avdataend']) {
170 $this->
fseek($info[
'la'][
'footerstart']);
171 $RIFFdata .= $this->
fread($info[
'avdataend'] -
$info[
'la'][
'footerstart']);
172 }
173 $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
174 fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
175 fclose($RIFF_fp);
176
177 $getid3_temp =
new getID3();
178 $getid3_temp->openfile($RIFFtempfilename);
180 $getid3_riff->Analyze();
181
182 if (empty($getid3_temp->info['error'])) {
183 $info[
'riff'] = $getid3_temp->info[
'riff'];
184 } else {
185 $this->
warning(
'Error parsing RIFF portion of La file: '.implode($getid3_temp->info[
'error']));
186 }
187 unset($getid3_temp, $getid3_riff);
188 }
189 unlink($RIFFtempfilename);
190 }
191 }
192
193
194 $info[
'avdataend'] =
$info[
'avdataoffset'] +
$info[
'la'][
'footerstart'];
195 $info[
'avdataoffset'] =
$info[
'avdataoffset'] + $offset;
196
197 $info[
'la'][
'compression_ratio'] = (float) ((
$info[
'avdataend'] -
$info[
'avdataoffset']) /
$info[
'la'][
'uncompressed_size']);
198 $info[
'playtime_seconds'] = (float) (
$info[
'la'][
'samples'] /
$info[
'la'][
'sample_rate']) /
$info[
'la'][
'channels'];
199 if (
$info[
'playtime_seconds'] == 0) {
200 $this->
error(
'Corrupt LA file: playtime_seconds == zero');
201 return false;
202 }
203
204 $info[
'audio'][
'bitrate'] = (
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8 /
$info[
'playtime_seconds'];
205
206 $info[
'audio'][
'bits_per_sample'] =
$info[
'la'][
'bits_per_sample'];
207 break;
208
209 default:
210 if (substr($rawdata, $offset, 2) == 'LA') {
211 $this->
error(
'This version of getID3() ['.$this->getid3->version().
'] does not 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.');
212 } else {
213 $this->
error(
'Not a LA (Lossless-Audio) file');
214 }
215 return false;
216 break;
217 }
218
219 $info[
'audio'][
'channels'] =
$info[
'la'][
'channels'];
220 $info[
'audio'][
'sample_rate'] = (int)
$info[
'la'][
'sample_rate'];
221 $info[
'audio'][
'encoder'] =
'LA v'.$info[
'la'][
'version'];
222
223 return true;
224 }
fseek($bytes, $whence=SEEK_SET)
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
static LittleEndian2Int($byteword, $signed=false)