3 /////////////////////////////////////////////////////////////////
4 /// getID3() by James Heinrich <info@getid3.org> //
5 // available at https://github.com/JamesHeinrich/getID3 //
6 // or https://www.getid3.org //
7 // or http://getid3.sourceforge.net //
8 // see readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
11 // module.audio.dsf.php //
12 // module for analyzing dsf/DSF Audio files //
13 // dependencies: module.tag.id3v2.php //
15 /////////////////////////////////////////////////////////////////
17 getid3_lib
::IncludeDependency(GETID3_INCLUDEPATH
.'module.tag.id3v2.php', __FILE__
, true);
19 class getid3_dsf
extends getid3_handler
24 public function Analyze() {
25 $info = &$this->getid3
->info
;
27 $info['fileformat'] = 'dsf';
28 $info['audio']['dataformat'] = 'dsf';
29 $info['audio']['lossless'] = true;
30 $info['audio']['bitrate_mode'] = 'cbr';
32 $this->fseek($info['avdataoffset']);
33 $dsfheader = $this->fread(28 +
12);
36 $info['dsf']['dsd']['magic'] = substr($dsfheader, $headeroffset, 4);
39 if ($info['dsf']['dsd']['magic'] != $magic) {
40 $this->error('Expecting "'.getid3_lib
::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib
::PrintHexBytes($info['dsf']['dsd']['magic']).'"');
41 unset($info['fileformat']);
42 unset($info['audio']);
46 $info['dsf']['dsd']['dsd_chunk_size'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // should be 28
48 $info['dsf']['dsd']['dsf_file_size'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
50 $info['dsf']['dsd']['meta_chunk_offset'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
54 $info['dsf']['fmt']['magic'] = substr($dsfheader, $headeroffset, 4);
57 if ($info['dsf']['fmt']['magic'] != $magic) {
58 $this->error('Expecting "'.getid3_lib
::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib
::PrintHexBytes($info['dsf']['fmt']['magic']).'"');
61 $info['dsf']['fmt']['fmt_chunk_size'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // usually 52 bytes
63 $dsfheader .= $this->fread($info['dsf']['fmt']['fmt_chunk_size'] - 12 +
12); // we have already read the entire DSD chunk, plus 12 bytes of FMT. We now want to read the size of FMT, plus 12 bytes into the next chunk to get magic and size.
64 if (strlen($dsfheader) != ($info['dsf']['dsd']['dsd_chunk_size'] +
$info['dsf']['fmt']['fmt_chunk_size'] +
12)) {
65 $this->error('Expecting '.($info['dsf']['dsd']['dsd_chunk_size'] +
$info['dsf']['fmt']['fmt_chunk_size']).' bytes header, found '.strlen($dsfheader).' bytes');
68 $info['dsf']['fmt']['format_version'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "1"
70 $info['dsf']['fmt']['format_id'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "0" = "DSD Raw"
72 $info['dsf']['fmt']['channel_type_id'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
74 $info['dsf']['fmt']['channels'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
76 $info['dsf']['fmt']['sample_rate'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
78 $info['dsf']['fmt']['bits_per_sample'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
80 $info['dsf']['fmt']['sample_count'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
82 $info['dsf']['fmt']['channel_block_size'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
84 $info['dsf']['fmt']['reserved'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // zero-filled
88 $info['dsf']['data']['magic'] = substr($dsfheader, $headeroffset, 4);
91 if ($info['dsf']['data']['magic'] != $magic) {
92 $this->error('Expecting "'.getid3_lib
::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib
::PrintHexBytes($info['dsf']['data']['magic']).'"');
95 $info['dsf']['data']['data_chunk_size'] = getid3_lib
::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
97 $info['avdataoffset'] = $headeroffset;
98 $info['avdataend'] = $info['avdataoffset'] +
$info['dsf']['data']['data_chunk_size'];
101 if ($info['dsf']['dsd']['meta_chunk_offset'] > 0) {
102 $getid3_id3v2 = new getid3_id3v2($this->getid3
);
103 $getid3_id3v2->StartingOffset
= $info['dsf']['dsd']['meta_chunk_offset'];
104 $getid3_id3v2->Analyze();
105 unset($getid3_id3v2);
109 $info['dsf']['fmt']['channel_type'] = $this->DSFchannelTypeLookup($info['dsf']['fmt']['channel_type_id']);
110 $info['audio']['channelmode'] = $info['dsf']['fmt']['channel_type'];
111 $info['audio']['bits_per_sample'] = $info['dsf']['fmt']['bits_per_sample'];
112 $info['audio']['sample_rate'] = $info['dsf']['fmt']['sample_rate'];
113 $info['audio']['channels'] = $info['dsf']['fmt']['channels'];
114 $info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels'];
115 $info['playtime_seconds'] = ($info['dsf']['data']['data_chunk_size'] * 8) / $info['audio']['bitrate'];
121 * @param int $channel_type_id
125 public static function DSFchannelTypeLookup($channel_type_id) {
126 static $DSFchannelTypeLookup = array(
127 // interleaving order:
128 1 => 'mono', // 1: Mono
129 2 => 'stereo', // 1: Front-Left; 2: Front-Right
130 3 => '3-channel', // 1: Front-Left; 2: Front-Right; 3: Center
131 4 => 'quad', // 1: Front-Left; 2: Front-Right; 3: Back-Left; 4: Back-Right
132 5 => '4-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency
133 6 => '5-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Back-Left 5: Back-Right
134 7 => '5.1', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency; 5: Back-Left; 6: Back-Right
136 return (isset($DSFchannelTypeLookup[$channel_type_id]) ?
$DSFchannelTypeLookup[$channel_type_id] : '');