23 $ThisFileInfo[
'fileformat'] =
'ogg';
26 if (isset($ThisFileInfo[
'id3v2'])) {
27 $ThisFileInfo[
'warning'][] =
'Illegal ID3v2 tag present.';
29 if (isset($ThisFileInfo[
'id3v1'])) {
30 $ThisFileInfo[
'warning'][] =
'Illegal ID3v1 tag present.';
32 if (isset($ThisFileInfo[
'ape'])) {
33 $ThisFileInfo[
'warning'][] =
'Illegal APE tag present.';
39 fseek($fd, $ThisFileInfo[
'avdataoffset'], SEEK_SET);
42 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
45 $ThisFileInfo[
'error'][] =
'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.
' bytes (this might not be an Ogg-Vorbis file?)';
46 unset($ThisFileInfo[
'fileformat']);
47 unset($ThisFileInfo[
'ogg']);
51 $filedata = fread($fd, $oggpageinfo[
'page_length']);
54 if (substr($filedata, 0, 4) ==
'fLaC') {
56 $ThisFileInfo[
'audio'][
'dataformat'] =
'flac';
57 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'vbr';
58 $ThisFileInfo[
'audio'][
'lossless'] =
true;
60 } elseif (substr($filedata, 1, 6) ==
'vorbis') {
62 $ThisFileInfo[
'audio'][
'dataformat'] =
'vorbis';
63 $ThisFileInfo[
'audio'][
'lossless'] =
false;
65 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'packet_type'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
67 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'stream_type'] = substr($filedata, $filedataoffset, 6);
73 $ThisFileInfo[
'audio'][
'channels'] = $ThisFileInfo[
'ogg'][
'numberofchannels'];
76 if ($ThisFileInfo[
'ogg'][
'samplerate'] == 0) {
77 $ThisFileInfo[
'error'][] =
'Corrupt Ogg file: sample rate == zero';
80 $ThisFileInfo[
'audio'][
'sample_rate'] = $ThisFileInfo[
'ogg'][
'samplerate'];
81 $ThisFileInfo[
'ogg'][
'samples'] = 0;
82 $ThisFileInfo[
'ogg'][
'bitrate_average'] = 0;
93 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'vbr';
94 if ($ThisFileInfo[
'ogg'][
'bitrate_max'] == 0xFFFFFFFF) {
95 unset($ThisFileInfo[
'ogg'][
'bitrate_max']);
96 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'abr';
98 if ($ThisFileInfo[
'ogg'][
'bitrate_nominal'] == 0xFFFFFFFF) {
99 unset($ThisFileInfo[
'ogg'][
'bitrate_nominal']);
101 if ($ThisFileInfo[
'ogg'][
'bitrate_min'] == 0xFFFFFFFF) {
102 unset($ThisFileInfo[
'ogg'][
'bitrate_min']);
103 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'abr';
106 } elseif (substr($filedata, 0, 8) ==
'Speex ') {
110 $ThisFileInfo[
'audio'][
'dataformat'] =
'speex';
111 $ThisFileInfo[
'mime_type'] =
'audio/speex';
112 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'abr';
113 $ThisFileInfo[
'audio'][
'lossless'] =
false;
115 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_string'] = substr($filedata, $filedataoffset, 8);
116 $filedataoffset += 8;
117 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version'] = substr($filedata, $filedataoffset, 20);
118 $filedataoffset += 20;
119 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version_id'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
120 $filedataoffset += 4;
121 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'header_size'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
122 $filedataoffset += 4;
123 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'rate'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
124 $filedataoffset += 4;
125 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'mode'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
126 $filedataoffset += 4;
127 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'mode_bitstream_version'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
128 $filedataoffset += 4;
129 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'nb_channels'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
130 $filedataoffset += 4;
131 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'bitrate'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
132 $filedataoffset += 4;
133 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'framesize'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
134 $filedataoffset += 4;
135 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'vbr'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
136 $filedataoffset += 4;
137 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'frames_per_packet'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
138 $filedataoffset += 4;
139 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'extra_headers'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
140 $filedataoffset += 4;
141 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'reserved1'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
142 $filedataoffset += 4;
143 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'reserved2'] =
getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
144 $filedataoffset += 4;
146 $ThisFileInfo[
'speex'][
'speex_version'] = trim($ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'speex_version']);
147 $ThisFileInfo[
'speex'][
'sample_rate'] = $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'rate'];
148 $ThisFileInfo[
'speex'][
'channels'] = $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'nb_channels'];
149 $ThisFileInfo[
'speex'][
'vbr'] = (bool) $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'vbr'];
152 $ThisFileInfo[
'audio'][
'sample_rate'] = $ThisFileInfo[
'speex'][
'sample_rate'];
153 $ThisFileInfo[
'audio'][
'channels'] = $ThisFileInfo[
'speex'][
'channels'];
154 if ($ThisFileInfo[
'speex'][
'vbr']) {
155 $ThisFileInfo[
'audio'][
'bitrate_mode'] =
'vbr';
160 $ThisFileInfo[
'error'][] =
'Expecting either "Speex " or "vorbis" identifier strings, found neither';
161 unset($ThisFileInfo[
'ogg']);
162 unset($ThisFileInfo[
'mime_type']);
171 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
173 switch ($ThisFileInfo[
'audio'][
'dataformat']) {
176 $filedata = fread($fd, $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length']);
178 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'stream_type'] = substr($filedata, 1, 6);
185 $ThisFileInfo[
'error'][] =
'Failed to parse FLAC headers';
191 fseek($fd, $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_length'], SEEK_CUR);
203 if ($LastOggSpostion = strpos($LastChunkOfOgg,
'SggO')) {
204 fseek($fd, $ThisFileInfo[
'avdataend'] - ($LastOggSpostion + strlen(
'SggO')), SEEK_SET);
205 $ThisFileInfo[
'avdataend'] = ftell($fd);
207 $ThisFileInfo[
'ogg'][
'samples'] = $ThisFileInfo[
'ogg'][
'pageheader'][
'eos'][
'pcm_abs_position'];
208 if ($ThisFileInfo[
'ogg'][
'samples'] == 0) {
209 $ThisFileInfo[
'error'][] =
'Corrupt Ogg file: eos.number of samples == zero';
212 $ThisFileInfo[
'ogg'][
'bitrate_average'] = (($ThisFileInfo[
'avdataend'] - $ThisFileInfo[
'avdataoffset']) * 8) / ($ThisFileInfo[
'ogg'][
'samples'] / $ThisFileInfo[
'audio'][
'sample_rate']);
215 if (!empty($ThisFileInfo[
'ogg'][
'bitrate_average'])) {
216 $ThisFileInfo[
'audio'][
'bitrate'] = $ThisFileInfo[
'ogg'][
'bitrate_average'];
217 } elseif (!empty($ThisFileInfo[
'ogg'][
'bitrate_nominal'])) {
218 $ThisFileInfo[
'audio'][
'bitrate'] = $ThisFileInfo[
'ogg'][
'bitrate_nominal'];
219 } elseif (!empty($ThisFileInfo[
'ogg'][
'bitrate_min']) && !empty($ThisFileInfo[
'ogg'][
'bitrate_max'])) {
220 $ThisFileInfo[
'audio'][
'bitrate'] = ($ThisFileInfo[
'ogg'][
'bitrate_min'] + $ThisFileInfo[
'ogg'][
'bitrate_max']) / 2;
222 if (isset($ThisFileInfo[
'audio'][
'bitrate']) && !isset($ThisFileInfo[
'playtime_seconds'])) {
223 if ($ThisFileInfo[
'audio'][
'bitrate'] == 0) {
224 $ThisFileInfo[
'error'][] =
'Corrupt Ogg file: bitrate_audio == zero';
227 $ThisFileInfo[
'playtime_seconds'] = (float) ((($ThisFileInfo[
'avdataend'] - $ThisFileInfo[
'avdataoffset']) * 8) / $ThisFileInfo[
'audio'][
'bitrate']);
230 if (isset($ThisFileInfo[
'ogg'][
'vendor'])) {
231 $ThisFileInfo[
'audio'][
'encoder'] = preg_replace(
'/^Encoded with /',
'', $ThisFileInfo[
'ogg'][
'vendor']);
234 if ($ThisFileInfo[
'audio'][
'dataformat'] ==
'vorbis') {
237 if (preg_match(
'/^Xiph.Org/', $ThisFileInfo[
'audio'][
'encoder'])) {
239 if ($ThisFileInfo[
'audio'][
'bitrate_mode'] ==
'abr') {
242 $ThisFileInfo[
'audio'][
'encoder_options'] =
'-b '.round($ThisFileInfo[
'ogg'][
'bitrate_nominal'] / 1000);
244 } elseif (($ThisFileInfo[
'audio'][
'bitrate_mode'] ==
'vbr') && ($ThisFileInfo[
'audio'][
'channels'] == 2) && ($ThisFileInfo[
'audio'][
'sample_rate'] >= 44100) && ($ThisFileInfo[
'audio'][
'sample_rate'] <= 48000)) {
246 $ThisFileInfo[
'audio'][
'encoder_options'] =
'-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo[
'ogg'][
'bitrate_nominal']);
251 if (empty($ThisFileInfo[
'audio'][
'encoder_options']) && !empty($ThisFileInfo[
'ogg'][
'bitrate_nominal'])) {
252 $ThisFileInfo[
'audio'][
'encoder_options'] =
'Nominal bitrate: '.intval(round($ThisFileInfo[
'ogg'][
'bitrate_nominal'] / 1000)).
'kbps';
263 $oggheader[
'page_start_offset'] = ftell($fd);
267 while ((substr($filedata, $filedataoffset++, 4) !=
'OggS')) {
272 if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
279 $filedataoffset += strlen(
'OggS') - 1;
282 $filedataoffset += 1;
284 $filedataoffset += 1;
285 $oggheader[
'flags'][
'fresh'] = (bool) ($oggheader[
'flags_raw'] & 0x01);
286 $oggheader[
'flags'][
'bos'] = (bool) ($oggheader[
'flags_raw'] & 0x02);
287 $oggheader[
'flags'][
'eos'] = (bool) ($oggheader[
'flags_raw'] & 0x04);
290 $filedataoffset += 8;
292 $filedataoffset += 4;
294 $filedataoffset += 4;
296 $filedataoffset += 4;
298 $filedataoffset += 1;
299 $oggheader[
'page_length'] = 0;
300 for ($i = 0; $i < $oggheader[
'page_segments']; $i++) {
302 $filedataoffset += 1;
303 $oggheader[
'page_length'] += $oggheader[
'segment_table'][$i];
305 $oggheader[
'header_end_offset'] = $oggheader[
'page_start_offset'] + $filedataoffset;
306 $oggheader[
'page_end_offset'] = $oggheader[
'header_end_offset'] + $oggheader[
'page_length'];
307 fseek($fd, $oggheader[
'header_end_offset'], SEEK_SET);
315 $OriginalOffset = ftell($fd);
316 $CommentStartOffset = $OriginalOffset;
317 $commentdataoffset = 0;
318 $VorbisCommentPage = 1;
320 switch ($ThisFileInfo[
'audio'][
'dataformat']) {
322 $CommentStartOffset = $ThisFileInfo[
'ogg'][
'pageheader'][$VorbisCommentPage][
'page_start_offset'];
323 fseek($fd, $CommentStartOffset, SEEK_SET);
324 $commentdataoffset = 27 + $ThisFileInfo[
'ogg'][
'pageheader'][$VorbisCommentPage][
'page_segments'];
327 $commentdataoffset += (strlen(
'vorbis') + 1);
331 fseek($fd, $ThisFileInfo[
'flac'][
'VORBIS_COMMENT'][
'raw'][
'offset'] + 4, SEEK_SET);
332 $commentdata = fread($fd, $ThisFileInfo[
'flac'][
'VORBIS_COMMENT'][
'raw'][
'block_length']);
336 $CommentStartOffset = $ThisFileInfo[
'ogg'][
'pageheader'][$VorbisCommentPage][
'page_start_offset'];
337 fseek($fd, $CommentStartOffset, SEEK_SET);
338 $commentdataoffset = 27 + $ThisFileInfo[
'ogg'][
'pageheader'][$VorbisCommentPage][
'page_segments'];
348 $commentdataoffset += 4;
350 $ThisFileInfo[
'ogg'][
'vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
351 $commentdataoffset += $VendorSize;
354 $commentdataoffset += 4;
355 $ThisFileInfo[
'avdataoffset'] = $CommentStartOffset + $commentdataoffset;
357 $basicfields = array(
'TITLE',
'ARTIST',
'ALBUM',
'TRACKNUMBER',
'GENRE',
'DATE',
'DESCRIPTION',
'COMMENT');
358 for ($i = 0; $i < $CommentsCount; $i++) {
360 $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'dataoffset'] = $CommentStartOffset + $commentdataoffset;
362 if (ftell($fd) < ($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'dataoffset'] + 4)) {
363 $VorbisCommentPage++;
366 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
369 $AsYetUnusedData = substr($commentdata, $commentdataoffset);
372 $commentdata = substr($commentdata, 0, $commentdataoffset);
375 $commentdata .= str_repeat(
"\x00", 27 + $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_segments']);
376 $commentdataoffset += (27 + $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_segments']);
379 $commentdata .= $AsYetUnusedData;
388 $ThisFileInfo[
'avdataoffset'] = $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'dataoffset'] + $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size'] + 4;
390 $commentdataoffset += 4;
391 while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size']) {
392 if (($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size'] > $ThisFileInfo[
'avdataend']) || ($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size'] < 0)) {
393 $ThisFileInfo[
'error'][] =
'Invalid Ogg comment size (comment #'.$i.
', claims to be '.number_format($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size']).
' bytes) - aborting reading comments';
397 $VorbisCommentPage++;
400 $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']] = $oggpageinfo;
403 $AsYetUnusedData = substr($commentdata, $commentdataoffset);
406 $commentdata = substr($commentdata, 0, $commentdataoffset);
409 $commentdata .= str_repeat(
"\x00", 27 + $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_segments']);
410 $commentdataoffset += (27 + $ThisFileInfo[
'ogg'][
'pageheader'][$oggpageinfo[
'page_seqno']][
'page_segments']);
413 $commentdata .= $AsYetUnusedData;
420 $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size']);
421 $commentdataoffset += $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'size'];
423 if (!$commentstring) {
426 $ThisFileInfo[
'warning'][] =
'Blank Ogg comment ['.$i.
']';
428 } elseif (strstr($commentstring,
'=')) {
430 $commentexploded = explode(
'=', $commentstring, 2);
431 $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'key'] = strtoupper($commentexploded[0]);
432 $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'value'] = @$commentexploded[1];
433 $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'data'] = base64_decode($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'value']);
435 $ThisFileInfo[
'ogg'][
'comments'][strtolower($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'key'])][] = $ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'value'];
439 if (!$ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'image_mime'] || ($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'image_mime'] ==
'application/octet-stream')) {
440 unset($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'image_mime']);
441 unset($ThisFileInfo[
'ogg'][
'comments_raw'][$i][
'data']);
446 $ThisFileInfo[
'warning'][] =
'[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.
']: '.$commentstring;
454 if (isset($ThisFileInfo[
'ogg'][
'comments']) && is_array($ThisFileInfo[
'ogg'][
'comments'])) {
455 foreach ($ThisFileInfo[
'ogg'][
'comments'] as $index => $commentvalue) {
457 case 'rg_audiophile':
458 case 'replaygain_album_gain':
459 $ThisFileInfo[
'replay_gain'][
'album'][
'adjustment'] = (double) $commentvalue[0];
460 unset($ThisFileInfo[
'ogg'][
'comments'][$index]);
464 case 'replaygain_track_gain':
465 $ThisFileInfo[
'replay_gain'][
'track'][
'adjustment'] = (double) $commentvalue[0];
466 unset($ThisFileInfo[
'ogg'][
'comments'][$index]);
469 case 'replaygain_album_peak':
470 $ThisFileInfo[
'replay_gain'][
'album'][
'peak'] = (double) $commentvalue[0];
471 unset($ThisFileInfo[
'ogg'][
'comments'][$index]);
475 case 'replaygain_track_peak':
476 $ThisFileInfo[
'replay_gain'][
'track'][
'peak'] = (double) $commentvalue[0];
477 unset($ThisFileInfo[
'ogg'][
'comments'][$index]);
488 fseek($fd, $OriginalOffset, SEEK_SET);
494 static $SpeexBandModeLookup = array();
495 if (empty($SpeexBandModeLookup)) {
496 $SpeexBandModeLookup[0] =
'narrow';
497 $SpeexBandModeLookup[1] =
'wide';
498 $SpeexBandModeLookup[2] =
'ultra-wide';
500 return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
505 for ($i = 0; $i < $SegmentNumber; $i++) {
507 foreach ($OggInfoArray[
'segment_table'] as $key => $value) {
508 $segmentlength += $value;
514 return $segmentlength;
521 $nominal_bitrate = $nominal_bitrate / 1000;
523 if ($nominal_bitrate < 128) {
525 $qval = ($nominal_bitrate - 64) / 16;
526 } elseif ($nominal_bitrate < 256) {
528 $qval = $nominal_bitrate / 32;
529 } elseif ($nominal_bitrate < 320) {
531 $qval = ($nominal_bitrate + 256) / 64;
534 $qval = ($nominal_bitrate + 1300) / 180;
538 return round($qval, 1);