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.
'"';
31 $ThisFileInfo[
'fileformat'] =
'flac';
32 $ThisFileInfo[
'audio'][
'dataformat'] =
'flac';
33 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'vbr';
34 $ThisFileInfo[
'audio'][
'lossless'] =
true;
43 $METAdataBlockOffset = ftell($fd);
44 $METAdataBlockHeader = fread($fd, 4);
50 if ($METAdataBlockLength < 0) {
51 $ThisFileInfo[
'error'][] =
'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.
') at offset '.$METAdataBlockOffset;
55 $ThisFileInfo[
'flac'][$METAdataBlockTypeText][
'raw'] = array();
56 $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo[
'flac'][$METAdataBlockTypeText][
'raw'];
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);
66 switch ($METAdataBlockTypeText) {
90 case 'VORBIS_COMMENT':
91 $OldOffset = ftell($fd);
92 fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
94 fseek($fd, $OldOffset, SEEK_SET);
104 $ThisFileInfo[
'warning'][] =
'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.
') at offset '.$METAdataBlockOffset;
108 }
while ($METAdataLastBlockFlag ===
false);
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';
118 $ThisFileInfo[
'flac'][
'compression_ratio'] = $ThisFileInfo[
'flac'][
'compressed_audio_bytes'] / $ThisFileInfo[
'flac'][
'uncompressed_audio_bytes'];
122 if (isset($ThisFileInfo[
'flac'][
'STREAMINFO'][
'audio_signature'])) {
124 if ($ThisFileInfo[
'flac'][
'STREAMINFO'][
'audio_signature'] === str_repeat(
"\x00", 16)) {
126 $ThisFileInfo[
'warning'][] =
'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
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);
135 if (!preg_match(
'/^[0-9a-f]{32}$/', $ThisFileInfo[
'md5_data_source'])) {
136 unset($ThisFileInfo[
'md5_data_source']);
143 $ThisFileInfo[
'audio'][
'bits_per_sample'] = $ThisFileInfo[
'flac'][
'STREAMINFO'][
'bits_per_sample'];
144 if ($ThisFileInfo[
'audio'][
'bits_per_sample'] == 8) {
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';
150 if (!empty($ThisFileInfo[
'ogg'][
'vendor'])) {
151 $ThisFileInfo[
'audio'][
'encoder'] = $ThisFileInfo[
'ogg'][
'vendor'];
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';
167 return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] :
'reserved');
171 static $FLACapplicationIDLookup = array();
172 if (empty($FLACapplicationIDLookup)) {
174 $FLACapplicationIDLookup[0x46746F6C] =
'flac-tools';
175 $FLACapplicationIDLookup[0x46746F6C] =
'Sound Font FLAC';
177 return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] :
'reserved');
182 $ThisFileInfo[
'flac'][
'STREAMINFO'][
'min_block_size'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
184 $ThisFileInfo[
'flac'][
'STREAMINFO'][
'max_block_size'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
186 $ThisFileInfo[
'flac'][
'STREAMINFO'][
'min_frame_size'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
188 $ThisFileInfo[
'flac'][
'STREAMINFO'][
'max_frame_size'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
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));
198 $ThisFileInfo[
'flac'][
'STREAMINFO'][
'audio_signature'] = substr($METAdataBlockData, $offset, 16);
201 if (!empty($ThisFileInfo[
'flac'][
'STREAMINFO'][
'sample_rate'])) {
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'];
212 $ThisFileInfo[
'error'][] =
'Corrupt METAdata block: STREAMINFO';
225 $ThisFileInfo[
'flac'][
'APPLICATION'][$ApplicationID][
'data'] = substr($METAdataBlockData, $offset);
226 $offset = $METAdataBlockLength;
234 $METAdataBlockLength = strlen($METAdataBlockData);
235 $placeholderpattern = str_repeat(
"\xFF", 8);
236 while ($offset < $METAdataBlockLength) {
237 $SampleNumberString = substr($METAdataBlockData, $offset, 8);
239 if ($SampleNumberString == $placeholderpattern) {
242 @$ThisFileInfo[
'flac'][
'SEEKTABLE'][
'placeholders']++;
248 $ThisFileInfo[
'flac'][
'SEEKTABLE'][$SampleNumber][
'offset'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
250 $ThisFileInfo[
'flac'][
'SEEKTABLE'][$SampleNumber][
'samples'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
260 $ThisFileInfo[
'flac'][
'CUESHEET'][
'media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128),
"\0");
262 $ThisFileInfo[
'flac'][
'CUESHEET'][
'lead_in_samples'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
264 $ThisFileInfo[
'flac'][
'CUESHEET'][
'flags'][
'is_cd'] = (bool) (
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
272 for ($track = 0; $track < $ThisFileInfo[
'flac'][
'CUESHEET'][
'number_tracks']; $track++) {
278 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'sample_offset'] = $TrackSampleOffset;
280 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'isrc'] = substr($METAdataBlockData, $offset, 12);
285 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'flags'][
'is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
286 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'flags'][
'pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
290 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'index_points'] =
getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
293 for ($index = 0; $index < $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'index_points']; $index++) {
301 $ThisFileInfo[
'flac'][
'CUESHEET'][
'tracks'][$TrackNumber][
'indexes'][$IndexNumber] = $IndexSampleOffset;