1<?php 2 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///////////////////////////////////////////////////////////////// 10// // 11// module.audio.dsf.php // 12// module for analyzing dsf/DSF Audio files // 13// dependencies: module.tag.id3v2.php // 14// /// 15///////////////////////////////////////////////////////////////// 16 17if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers 18 exit; 19} 20getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); 21 22class getid3_dsf extends getid3_handler 23{ 24 /** 25 * @return bool 26 */ 27 public function Analyze() { 28 $info = &$this->getid3->info; 29 30 $info['fileformat'] = 'dsf'; 31 $info['audio']['dataformat'] = 'dsf'; 32 $info['audio']['lossless'] = true; 33 $info['audio']['bitrate_mode'] = 'cbr'; 34 35 $this->fseek($info['avdataoffset']); 36 $dsfheader = $this->fread(28 + 12); 37 38 $headeroffset = 0; 39 $info['dsf']['dsd']['magic'] = substr($dsfheader, $headeroffset, 4); 40 $headeroffset += 4; 41 $magic = 'DSD '; 42 if ($info['dsf']['dsd']['magic'] != $magic) { 43 $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dsf']['dsd']['magic']).'"'); 44 unset($info['fileformat']); 45 unset($info['audio']); 46 unset($info['dsf']); 47 return false; 48 } 49 $info['dsf']['dsd']['dsd_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // should be 28 50 $headeroffset += 8; 51 $info['dsf']['dsd']['dsf_file_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 52 $headeroffset += 8; 53 $info['dsf']['dsd']['meta_chunk_offset'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 54 $headeroffset += 8; 55 56 57 $info['dsf']['fmt']['magic'] = substr($dsfheader, $headeroffset, 4); 58 $headeroffset += 4; 59 $magic = 'fmt '; 60 if ($info['dsf']['fmt']['magic'] != $magic) { 61 $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['fmt']['magic']).'"'); 62 return false; 63 } 64 $info['dsf']['fmt']['fmt_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // usually 52 bytes 65 $headeroffset += 8; 66 $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. 67 if (strlen($dsfheader) != ($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size'] + 12)) { 68 $this->error('Expecting '.($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size']).' bytes header, found '.strlen($dsfheader).' bytes'); 69 return false; 70 } 71 $info['dsf']['fmt']['format_version'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "1" 72 $headeroffset += 4; 73 $info['dsf']['fmt']['format_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "0" = "DSD Raw" 74 $headeroffset += 4; 75 $info['dsf']['fmt']['channel_type_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 76 $headeroffset += 4; 77 $info['dsf']['fmt']['channels'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 78 $headeroffset += 4; 79 $info['dsf']['fmt']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 80 $headeroffset += 4; 81 $info['dsf']['fmt']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 82 $headeroffset += 4; 83 $info['dsf']['fmt']['sample_count'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 84 $headeroffset += 8; 85 $info['dsf']['fmt']['channel_block_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 86 $headeroffset += 4; 87 $info['dsf']['fmt']['reserved'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // zero-filled 88 $headeroffset += 4; 89 90 91 $info['dsf']['data']['magic'] = substr($dsfheader, $headeroffset, 4); 92 $headeroffset += 4; 93 $magic = 'data'; 94 if ($info['dsf']['data']['magic'] != $magic) { 95 $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['data']['magic']).'"'); 96 return false; 97 } 98 $info['dsf']['data']['data_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 99 $headeroffset += 8; 100 $info['avdataoffset'] = $headeroffset; 101 $info['avdataend'] = $info['avdataoffset'] + $info['dsf']['data']['data_chunk_size']; 102 103 104 if ($info['dsf']['dsd']['meta_chunk_offset'] > 0) { 105 $getid3_id3v2 = new getid3_id3v2($this->getid3); 106 $getid3_id3v2->StartingOffset = $info['dsf']['dsd']['meta_chunk_offset']; 107 $getid3_id3v2->Analyze(); 108 unset($getid3_id3v2); 109 } 110 111 112 $info['dsf']['fmt']['channel_type'] = $this->DSFchannelTypeLookup($info['dsf']['fmt']['channel_type_id']); 113 $info['audio']['channelmode'] = $info['dsf']['fmt']['channel_type']; 114 $info['audio']['bits_per_sample'] = $info['dsf']['fmt']['bits_per_sample']; 115 $info['audio']['sample_rate'] = $info['dsf']['fmt']['sample_rate']; 116 $info['audio']['channels'] = $info['dsf']['fmt']['channels']; 117 $info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels']; 118 $info['playtime_seconds'] = ($info['dsf']['data']['data_chunk_size'] * 8) / $info['audio']['bitrate']; 119 120 return true; 121 } 122 123 /** 124 * @param int $channel_type_id 125 * 126 * @return string 127 */ 128 public static function DSFchannelTypeLookup($channel_type_id) { 129 static $DSFchannelTypeLookup = array( 130 // interleaving order: 131 1 => 'mono', // 1: Mono 132 2 => 'stereo', // 1: Front-Left; 2: Front-Right 133 3 => '3-channel', // 1: Front-Left; 2: Front-Right; 3: Center 134 4 => 'quad', // 1: Front-Left; 2: Front-Right; 3: Back-Left; 4: Back-Right 135 5 => '4-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency 136 6 => '5-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Back-Left 5: Back-Right 137 7 => '5.1', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency; 5: Back-Left; 6: Back-Right 138 ); 139 return (isset($DSFchannelTypeLookup[$channel_type_id]) ? $DSFchannelTypeLookup[$channel_type_id] : ''); 140 } 141 142} 143